Debugging Classic ASP with Visual Studio 2010

In order to be able to set breakpoints that actually work in classic ASP with Visual Studio 2010, here are the steps i had to take.
1. Install WebMatrix (in order to get IISExpress)
2. Right-click the website project and choose “Use IISExpress”.
3. Go to the project properties (F4 when project highlighted)
4. Set “SSL Enabled” to True
5. Open the applicationhost.config found here: c:\Users\YOU\Documents\IISExpress\config\
6. Under <system.webServer> change the <asp> section to be:
<asp appAllowDebugging="true" appAllowClientDebug="true" scriptErrorSentToBrowser="true">
<cache diskTemplateCacheDirectory="%TEMP%\iisexpress\ASP Compiled Templates" />
<limits />
</asp>
7. Close/open Visual Studio
8. Run the project “without debugging” (Ctrl+F5)
9. Browse to the HTTPS URL https://localhost:44300/
10. Attach to the IISExpress process manually (Ctrl+Alt+P)
11. Make sure the checkboxes for “Show processes from all users” and “Show processes in all sessions” are both checked
12. Choose “iisexpress.exe” and Attach
You should be good to go.
Advertisements
Posted in Classic ASP, Visual Studio 2010 | Tagged , , | 1 Comment

Simple IDataReader ORM extension methods

Instead of deploying an entire ORM solution and because I’m lazy, I created (read: google-distilled) these simple extension methods to map an IDataReader into a IEnumerable<T>. So basically, I can map any result set (that is an IDataReader) into a list of strongly typed objects of my choosing.

For example, to convert my IDataReader object containing account information into an IEnumerable set of “Account” objects, I’d do this:

        public virtual IEnumerable<Account> GetAccountBalances(User user)
        {
            return DataAccess.GetFundBalances(user.Id) // Returns an IDataReader
                    .MapTo(x => new Account()
                    {
                        // "x" is a IDataRecord object
                        Id = x["id"].ToString(),
                        Type = x["type"].ToString(),
                        PlanType = x["plantype"].ToString(),
                        PlanDescription = x["plandescription"].ToString(),
                        Balance = x["balance"]
                    });

        }

The “MapTo” extension method accepts the delegate “CreateObjectFromDataRecord” which can be passed in with a lambda expression to neatly create the strongly typed object from the row of data (IDataRecord) inline.

Here’s the code for the extension methods:

    public static class DataReaderExtensions
    {
        public delegate T CreateObjectFromDataRecord<T>(IDataRecord record);

        public static IEnumerable<T> MapTo<T>(this IDataReader reader, CreateObjectFromDataRecord<T> createobj)
        {
            var targets = new List<T>();
            
            while (reader.Read())
                targets.Add(createobj(reader));

            return targets;
        }

        public static T MapFirstTo<T>(this IDataReader reader, CreateObjectFromDataRecord<T> createobj)
        {
            T target = default(T);

            if (reader.Read())
                target = createobj(reader);

            return target;
        }
    }
Posted in .NET, C#, extension methods, IDataReader, ORM | Leave a comment

My Regex Function Parser

AND THEY SAID IT COULDN’T BE DONE!… or was that “shouldn’t”?
Oh well, in any case, here is a FunctionParser I made to parse text functions.
For example, this:


var MyText = "Here is my text. It has @replace(""some text in which I’d like to replace this"",""this"",""withthat""). I'd also like to know what time it is @now(""MMM dd, yyyy"") and what it would look like if i formatted a date like this @formatdate(""4/16/2010"",""yyyyMMdd"")";

Console.Write(FunctionParser.Process(MyText));

would output:

“Here is my text. It has some text in which I’d like to replace withthat. I’d also like to know what time it is Oct 26, 2010 and what it would look like if i formatted a date like this 20100416”

using System;
using System.Collections;
using System.Collections.Specialized;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Web.UI;

namespace MyCompany.Core.Text
{
    /// <summary>
    /// This class is used to parse function in a text string. It handles replacing the
    /// function placeholders such as, @replace("sometex","x","xt), with the function result.
    /// </summary>
    public static class FunctionParser
    {
        private const string _MatchingParentheses = @"\((?>[^()]+|\((?<number>)|\)(?<-number>))*(?(number)(?!))\)";
        private const string _NestedFunctions = @"@(?<function>\w+)\(((?<arg>\""[^\""]*\""|\d+|(?<nestedfunction>@\w+" + _MatchingParentheses + @"))(?:,?))+\)";
        private static Regex _rxFunction = new Regex(_NestedFunctions, RegexOptions.Compiled | RegexOptions.Singleline);
        private static Regex _rxBooleanExpression = new Regex(@"(?<operand1>[^=<>!]+)\s*(?<operator>[=<>!]+)\s*(?<operand2>[^=<>!]+)", RegexOptions.Compiled | RegexOptions.Singleline);
        private static Regex _rxVariable = new Regex(@"@\{[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}\}", RegexOptions.Compiled | RegexOptions.Singleline);

        public static string Process(string text)
        {
            return Process(text, null, null);
        }

        public static string Process(string text, CustomFunctionHandler fxHandler, StringDictionary variables)
        {
            string result = string.Empty;
            try
            {
                result = Process(text, fxHandler, 0, variables);
            }
            catch (Exception ex)
            {
                Log.Error("FunctionParser error. Input: " + text, ex);
                result = "FunctionParser error: " + ex.Message;
            }
            return result;
        }

        private static string Process(string text, CustomFunctionHandler fxHandler, int stack, StringDictionary variables)
        {
            stack++;
            if (stack >= 10)
            {
                throw new Exception("FunctionParser max recursion level (10) exceeded!");
            }

            string result = _rxFunction.Replace(text, delegate(Match m)
            {
                // Preset the return value
                string replacewith = "'@" + m.Groups["function"].Value + "' function is not recognized.";
                string[] args = m.Groups["arg"].Captures
                                    .Cast<Capture>()
                                    .Select<Capture, string>(x => x.Value.TrimStart(new char[] { '"' }).TrimEnd(new char[] { '"' }))
                                    .ToArray();

                // Resolve variables...
                if (variables != null && variables.Count > 0)
                {
                    // replace variables in each arg with the value
                    args = args.Select(delegate(string arg)
                            {
                                return _rxVariable.Replace(arg, x => variables.ContainsKey(x.Value) ? variables[x.Value] : string.Empty);
                            })
                            .ToArray();
                }

                // Process nested functions first...
                List<string> nestedfunctions = m.Groups["nestedfunction"].Captures
                                                    .Cast<Capture>()
                                                    .Select<Capture, string>(x => x.Value)
                                                    .ToList();
                if (nestedfunctions.Count > 0)
                {
                    for (int i = 0; i < args.Length; i++)
                    {
                        if (nestedfunctions.Contains(args[i]))
                            args[i] = Process(args[i], fxHandler, stack, variables);
                    }
                }

                switch (m.Groups["function"].Value)
                {
                    case "replace":
                        replacewith = args[0].Replace(args[1], args[2]);
                        break;
                    case "formatdate":
                        replacewith = FormatDate(args[0], args[1]);
                        break;
                    case "now":
                        replacewith = DateTime.Now.ToString(args[0]);
                        break;
                    case "if":
                        if (EvaluateBooleanExpression(args[0]))
                            replacewith = args[1];
                        else
                            replacewith = args[2];
                        break;
                    case "dateadd":
                        replacewith = DateAdd(args[0], args[1]);
                        break;
                    case "substring":
                        int startIndex = int.Parse(args[1]);
                        if (args.Length == 2)
                            replacewith = args[0].Substring(startIndex);
                        else if (args.Length == 3)
                        {
                            int length = int.Parse(args[2]);
                            replacewith = args[0].Substring(startIndex, Math.Min(args[0].Length - startIndex, length));
                        }
                        break;
                    default:
                        if (fxHandler != null)
                            replacewith = fxHandler(m.Groups["function"].Value, args);
                        break;
                }

                return replacewith;
            });

            return result;
        }

        public static bool EvaluateBooleanExpression(string expr)
        {
            bool result = false;

            Match m = _rxBooleanExpression.Match(expr);
            if (m.Success)
            {
                string op1 = m.Groups["operand1"].Value.Trim();
                string op2 = m.Groups["operand2"].Value.Trim();

                switch (m.Groups["operator"].Value)
                {
                    case "=":
                    case "==":
                        result = op1 == op2;
                        break;
                    case "!=":
                        result = op1 != op2;
                        break;
                }
            }

            return result;
        }

        private static string FormatDate(string datestring, string formatstring)
        {
            string result = string.Empty;
            DateTime date;

            if (DateTime.TryParse(datestring, out date))
                result = date.ToString(formatstring);

            return result;
        }

        private static string DateAdd(string timespan, string datestring)
        {
            string result = string.Empty;
            DateTime date;
            TimeSpan ts;

            if (DateTime.TryParse(datestring, out date))
            {
                if (TimeSpan.TryParse(timespan, out ts))
                    result = date.Add(ts).ToString();

            }

            return result;
        }
    }
}

The CustomFunctionDelegate allows you to pass in the implementation for your own custom functions (although you could just as easily modify this class to add it in).

The “variables” dictionary is used in the case you were passing in your function parameters as variables instead of inline text. So instead of:

@replace(“some text in which I’d like to replace this”,”this”,”withthat”)

this:

@replace(“@MyVar1″,”@MyVar2″,”@MyVar3”)

and the string dictionary:

@MyVar1 = “some text in which I’d like to replace this”

@MyVar2 = “this”

@MyVar3 = “withthat”

There you have it. Use it at your own risk. 🙂

Posted in .NET, C#, regex | Leave a comment

Attribute driven caching in ASP.NET (using Ninject)

So it turns out Ninject is AWESOME.

As such, I’ve implemented a completely attribute driven caching scheme for my data service classes. My previous post mentioned a way to condense the code needed to retrieve data from ASP.NET’s data cache. That was great and all, but I didn’t like having to add all that extra code for each method I wanted cached. It’d be much better if I could just “intercept” all method calls to a particular class and put the caching code in there, and control the timeout through attributes.

Enter Ninject.extensions.interception.

The basic overview is that you can use this Ninject extension to intercept calls to methods or entire classes in your code. It does this by dynamically creating a proxy class to your underlying class/methods and calls that instead of your class directly. (At least… this is the mental model that I’ve formed).. In any case, it works.

In the older version of Ninject there was an extension to do exactly what I’m doing here except that it used its own in memory cache. So to build my solution I just took the parts that were useful from this older solution and made it work with ASP.NET’s data cache instead.

First thing to do is download the caching library source.

UPDATE: The code has been moved to GitHub and some sample code has been added to show how to set it up in a WebForms application

Here’s how you set it up:

  1. Add “CacheAllVirtualMethods” attribute to the class
  2. Make all methods you want cached “virtual” methods
  3. Add “CacheTimeout” attribute to any methods whose timeout you want different than the default
  4. If you don’t want a method cached, don’t make it virtual… or add the “DoNotCache” attribute to it

[CacheAllVirtualMethods(DefaultTimeoutMinutes = 10)]
public class MyDataService : BaseService, IMyDataService{
 public virtual User GetUser(string userid)    {
 //.....
 }
 [CacheTimeout(TimeoutMinutes = 2)]
 public virtual IEnumerable<Account> GetccountBalances(User user)    {
 //.....
 }
 [CacheTimeout(TimeoutMinutes = 60)]
 public virtual IEnumerable<Announcement> GetAnnouncements()    {
 return GetAnnouncements(null);
 }
}

Posted in .NET, cache, ninject | 8 Comments

Using C# delegates with ASP.NET data caching

When using the data cache in ASP.NET, the standard practice is to: 

  1. Check if the object exists in the data cache
  2. If not, then create/generate object and store the new object in the cache for the next time
  3. Return the object

 In an effort to condense this code,  I’ve come up with the following CacheManager base class:

public abstract class CacheManager : ICacheManager
    {

        public CacheManager()
        {
            TimeoutMinutes = 60;
            AbsoluteExpiration = System.Web.Caching.Cache.NoAbsoluteExpiration;
            Dependency = null;
            ItemPriority = CacheItemPriority.Default;
            ItemRemovedCallback = null;
        }

        public double TimeoutMinutes { get; set; }
        public DateTime AbsoluteExpiration { get; set; }
        public CacheDependency Dependency { get; set; }
        public CacheItemPriority ItemPriority { get; set; }
        public CacheItemRemovedCallback ItemRemovedCallback { get; set; }

        protected virtual string KeyPrefix
        {
            get
            {
                return string.Empty;
            }
        }

        private string FullKey(string key)
        {
            if (!string.IsNullOrEmpty(this.KeyPrefix))
                return this.KeyPrefix + "." + key;
            else
                return key;
        }

        public void Add(object cacheObject, string key)
        {
            this.Add(cacheObject, key, this.TimeoutMinutes);
        }
        private void Add(object cacheObject, string key, double minutes)
        {
            HttpContext.Current.Cache.Insert(FullKey(key), cacheObject,
                                    this.Dependency,
                                    this.AbsoluteExpiration,
                                    TimeSpan.FromMinutes(minutes),
                                    this.ItemPriority,
                                    this.ItemRemovedCallback);
        }

        public void Remove(string key)
        {
            HttpContext.Current.Cache.Remove(FullKey(key));
        }

        public bool Exist(string key)
        {
            return HttpContext.Current.Cache[FullKey(key)] != null;
        }

        public virtual T Get<T>(string key) where T : class
        {
            return HttpContext.Current.Cache[FullKey(key)] as T;
        }

        public virtual T Get<T>(string key, Func<T> fn) where T : class
        {
            return this.Get<T>(key, this.TimeoutMinutes, fn);
        }
        public virtual T Get<T>(string key, double timeoutminutes, Func<T> fn) where T : class
        {
            var obj = this.Get<T>(key);
            if (obj == default(T))
            {
                        obj = fn();
                        this.Add(obj, key, timeoutminutes);
            }

            return obj;
        }
    }

The key methods to notice are the “Get” methods. They allow passing in an anonymous delegate so that steps 1 through 3 from above are condensed into a single method call. The following code would retrieve the “book” object from the data cache or the data access layer and return it:


var book = ServiceDataCache.Get("GetBook." + authorName, 60, delegate()
{
    return DataAccess.GetBook(authorName);

});

Posted in .NET, C#, cache | 4 Comments