Monday, July 31, 2006

The little guy helps out Microsoft

It seems Microsoft is having trouble paying the (bandwidth) bills and along comes the "little guy" to help them out. I've previously mentioned Red Swoosh, a BitTorrent-like product that lets you proxy your files automatically so they get downloaded from any peers that currently have the file (or parts). This very similar to BitTorrent; the differences are that the download happens inside your browser, it doesn't require special preperation (as the Red Swoosh site handles the seeding), and requires only a tiny download. In fact, the latest version 2.0 of the Red Swoosh client has shrunk from 600KB to 72 KB!

So, if you want to download the Office 2007 beta without paying Microsoft $1.50 to cover thier bandwidth costs, just click me. [Via: Get the Microsoft Office Professional 2007 Beta]

Friday, July 28, 2006

Fixing forums.asp.net RSS feed issues.

I use RSSBandit as my feed reader, and it is awesome. I noticed, however, that it was having issues with the forum feeds from ASP.Net. A typical feed from there looks like this http://forums.asp.net/rss.aspx?ForumID=1022&Mode=0 (not sure what that Mode=0 does... anyone care to document that). This works fine the first time I hit the page, but subsequent to that CommunityServer (which is what forums.asp.net runs on) will always return a 304 Not Modified HTTP status code. This is clearly a lie when the forum has new messages. For a while, I would just tweek the feed url changing the Mode parameter to 1 (no idea what this does), do a refresh, and then change it back. This would cause a URL mismatch, so we would get the feed from the server anew.

This got a little old after a while, so I fired up Fiddler and verified what I needed to tweek to insure the fetch succeeds. If I zap the If-Modified-Since and If-None-Match header elements from the request headers, then I'll always get back the feed. This is somewhat bad manners, of course, but until the folks at Telligent fix the problem in CommunityServer and the folks at Microsoft update the software at asp.net, I'll just have to keep poking them.

The code for Fiddler's script looks like this:

// up in the top of the class Handlers
public static RulesOption("Fix &RSS forums.asp.net")
var m_RSSForum: boolean = true;
 
// in OnBeforeRequest
if (m_RSSForum
    && oSession.host.indexOf("forums.asp.net")>-1
    && oSession.url.toLowerCase().indexOf("/rss.aspx")>-1)
{
    oSession.oRequest.headers.Remove("If-Modified-Since");
    oSession.oRequest.headers.Remove("If-None-Match");
}

Now that I run Fiddler all the time, this just works (which I do to strip ads from everything I surf). I hope this helps you.

Thursday, July 27, 2006

Updated files for Dynamic method caller available...

I've updated the Utilities.zip and DynamicComparer.zip files with my latest cleanup of the DynamicCall stuff. This version fully supports static methods is much simpler, uses less working set and is faster. A full post will follow on all the changes soon.

BREAKING CHANGE

This version has an entirely new syntax that I think is clearer, plus it leads to much better generic-specialization / JIT memory usage. The new syntax looks like this:

Func<Person, bool, Person> compatible = Dynamic<Person>.Instance.Function<bool>.Explicit<Person>.CreateDelegate("Compatible");
Func<Person, Person, Person, Gender> breed = Dynamic<Person>.Instance.Function<Person>.Explicit<Person, Gender>.CreateDelegate("Breed");
Proc<Person> mutate = Dynamic<Person>.Instance.Procedure.Explicit.CreateDelegate("Mutate");
StaticFunc<Person, int, Person, Person> ageDifference = Dynamic<Person>.Static.Function<int>.Explicit<Person, Child>.CreateDelegate("AgeDifference");
Or, if you use the new C# 3.0 var syntax:
var compatible = Dynamic<Person>.Instance.Function<bool>.Explicit<Person>.CreateDelegate("Compatible");
var breed = Dynamic<Person>.Instance.Function<Person>.Explicit<Person, Gender>.CreateDelegate("Breed");
var mutate = Dynamic<Person>.Instance.Procedure.Explicit.CreateDelegate("Mutate");
var ageDifference = Dynamic<Person>.Static.Function<int>.Explicit<Person, Child>.CreateDelegate("AgeDifference");
I'm sorry for any trouble this might cause, but I think it is worth it. If you have any comments, I'm listening...

UPDATE (27-July-2007 15:32): I changed the Build to skip access-checks when creating the DynamicMethod. This allows you to bind against private methods of classes even when the bound-to class is not on the inheritance path. Also fixed a bug with instance methods that are functions requiring no parameters not always binding correctly. If you downloaded before 15:30 CDT, please download again.

Tuesday, July 25, 2006

Things to remember when creating delegates and doing LCG...

In my AcquiringObjectDataSource articles part three I promised to show what things look like using the DynamicCall library I've previously posted. In doing that, I've had to make the DynamicCall stuff work with static methods. Previously I had inserted some code to start handling statics deep inside the Build methods, but I've certainly never gotten to the point of using them. Sorry to anyone that tried, I should have documented that code as not-yet working.

Anyway, last night I worked into the wee hours of the morning getting everything hooked up for static methods. In the process I learned some things that I thought I would dump now. In an upcoming post, I'll talk about the changes to the DynamicCall library, but for now:

  • When you create a delegate for a static method, the delegate signature should not include a "this" reference. Duh. This manifests itself in the error message below when calling CreateDelegate. This happens because the delegate definition expects a "this" pointer as the first method argument, but the generated DynamicMethod doesn't have one. This spawned a whole new set of StaticProc and StaticFunc delegate definitions.
    ArgumentException: Error binding to target method
  • If you get the delegate definition correct, but don't generate the right code, it'll hurl at JIT with the message below. Note that the generation of the LCG DynamicMethod is fine, the delegate binding is fine. The JIT of the first caller to that delegate is where things will pop. This is often very in the runtime, which is bad.
    System.InvalidProgramException: Common Language Runtime detected an invalid program.
  • Not strictly related to static method supprt, but I rediscovered that you can't access private members of a class unless the DynamicMethod was properly bound to that class. This resulted in me still having to know the class of the static method, even though there's no "this" pointer to deal with.
  • Ruthless refactoring makes things much easier. Doing this work on the DynamicCall library has really cleaned things up, I'll give more detail in a later post.

As it stands now binding to a static method now looks like this:

StaticProc<ObjectDataSourceView, ParameterCollection, IDictionary, IDictionary> s_MergeDictionaries =
    DynamicProcedure<ObjectDataSourceView, ParameterCollection, IDictionary, IDictionary>.Static.Initialize("MergeDictionaries", CreateParameterList.Auto);
    Dynamic<ObjectDataSourceView>.Static.Procedure.Explicit<ParameterCollection, IDictionary, IDictionary>.CreateDelegate("MergeDictionaries", CreateParameterList.Auto);

While this is really clear (I think), it is very verbose. The new C# 3.0 "var" feature will will make this much more readable:

var s_MergeDictionaries =
    DynamicProcedure<ObjectDataSourceView, ParameterCollection, IDictionary, IDictionary>.Static.Initialize("MergeDictionaries", CreateParameterList.Auto);
    Dynamic<ObjectDataSourceView>.Static.Procedure.Explicit<ParameterCollection, IDictionary, IDictionary>.CreateDelegate("MergeDictionaries", CreateParameterList.Auto);

UPDATE (27-July-2007 15:32): New syntax based on the latest round of refactorings on the DynamicCallDynamic<> class.

Monday, July 24, 2006

I can see clearly now, the pain is gone (or how one little virtual makes the whole view clear)

In part one of this little series, I introduced the first player in our little play, AcquringObjectDataSource. Quite literally, the only reason that class was necessary was to insure that we create a slightly smarter ObjectDataSourceView. This part of the series is all about adding the "acquire" behavior. What I'm after is a simple way of getting the choice as to whether the DataObjectType instance is created ex-nilo via Activator.CreateInstance (the standard behavior of ObjectDataSourceView, or created by a factory class, or recalled from some cache or datastore. In my case, I want to reload the object instances from the database via Wilson's O/R Mapper, after first having checked the ASP.Net Session to see if we've got partial changes to carry through.

The View does the work

The first thing that becomes painfully obvious when building on ODS is that the ODSV object does the heavy lifting. Almost everything in ODS that isn't related to the creation of the view, or the maintenance of the cache is merely a call-through proxy to the ODSV. The main operations are obviously the CRUD operations. These operations are carried out but the ExecuteInsert, ExecuteSelect, ExecuteUpdate and ExecuteDelete, respectively. The read method (ExecuteSelect) already acts as expected, so we'll ignore that method. The others mutating methods are handed an IDictionary of keys, old values and new values (each method gets some of these, as appropriate to the use). The dictionaries are used to fill in values for a new object (in case of insert), an old object (in case of delete), or both (in case of update). This is, as stated, where my pain begins. I want to insure that we hit whatever the original data source is if needed, not create the objects out of the ether. In each of the mutating methods it builds up a list of parameter values by merging in any hard-coded parameters values, the keys IDictionary and any values IDictionary(s). This is done by a private method of ODSV called BuildDataObject. This simple method looks like this in Reflector (I've highlighted the line that grieves my soul):

private object BuildDataObject(Type dataObjectType, IDictionary inputParameters)
{
      object obj1 = Activator.CreateInstance(dataObjectType);
      PropertyDescriptorCollection collection1 = TypeDescriptor.GetProperties(obj1);
      foreach (DictionaryEntry entry1 in inputParameters)
      {
            string text1 = (entry1.Key == null) ? string.Empty : entry1.Key.ToString();
            PropertyDescriptor descriptor1 = collection1.Find(text1, true);
            if (descriptor1 == null)
            {
                  throw new InvalidOperationException(SR.GetString("ObjectDataSourceView_DataObjectPropertyNotFound", new object[] { text1, this._owner.ID }));
            }
            if (descriptor1.IsReadOnly)
            {
                  throw new InvalidOperationException(SR.GetString("ObjectDataSourceView_DataObjectPropertyReadOnly", new object[] { text1, this._owner.ID }));
            }
            object obj2 = ObjectDataSourceView.BuildObjectValue(entry1.Value, descriptor1.PropertyType, text1);
            descriptor1.SetValue(obj1, obj2);
      }
      return obj1;
}
If it weren't for that simple little Activator.CreateInstance, my life would have been easy... but, alas, I must toil against the oppressors. I could easily replace that method, but for several things. First, it is not virtual, so my version would not get called. Second, it is private, so I can't very well call down to reuse the behavior. Third, it uses ObjectDataSourceView.BuildObjectValue, which is of course private.

Virtual is where the action is

What I need to do is make sure that I have an integration-point where I can embrace and extend the Microsoft implementation. I want to make sure that some event or virtual method is called to create / rehydrate / lookup the actual data object. How about something blindingly obvious like:

public virtual object AcquireDataObject(Type dataObjectType, IDictionary inputParameters)
{
    return Activator.CreateInstance(dataObjectType);
}
This has the advantage of being hookable, while still letting the original naive implementation to be used. [Note: If Microsoft had simply done this and ONLY this to the original ODSV class, I wouldn't have needed any of this, not that I'm bitter]. We pass the IDictionary of input parameters to the creation method to give it a place to extract the key values, if needed.

Of course, it can't be that easy for me. Rather, I get bit by the fact that since BuildDataObject is non-virtual, private and pretty much incestuously coupled with everything else in ODSV, I'm going to have to do much more work. Specifically, I need to insure that my version of BuildDataObject gets called, so that I can replace that first line with a call to the new virtual AcquireDataObject method. This means that I need to override the mutating methods ExecuteInsert, ExecuteUpdate and ExecuteDelete. Since I only care about the case where a DataObjectType has been specified, I can down-call if that value isn't set, and have a vastly simplified body for each of these that only handles the DOT case. Of course, to simply reimplement those three methods I have to call a boat-load of other methods in ODSV, which are (you guessed it) private. Namely I need access to the Owner field, the GetType method, the TryGetDataObjectType method, the MergeDictionaries method, the GetResolvedMethodData method, the previously mentioned BuildObjectValue method, the InvokeMethod method, and ickly-enough the private ObjectDataSourceMethod nested class; whose Parameters field we also need to fiddle with. Lastly, we need to be able to extract the AffectedRows of the private ObjectDataSourceResult nested class. That's a lot of tendrils stuck in to ODSV, but I for the want of a single virtual, that is the path I must tread.

Where we are now

So, without any further delay, may I introduce to you the fair and fleeting AcquiringObjectDataSourceView, whose merest existence I groan at:

public class AcquiringObjectDataSourceView : ObjectDataSourceView
{
    const BindingFlags NeedlesslyPrivate = BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly;
    const BindingFlags NeedlesslyPrivateStatic = BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.DeclaredOnly;
 
    static readonly FieldInfo s_Owner;
    static readonly MethodInfo s_GetType;
    static readonly MethodInfo s_TryGetDataObjectType;
    static readonly MethodInfo s_MergeDictionaries;
    static readonly MethodInfo s_GetResolvedMethodData;
    static readonly MethodInfo s_InvokeMethod;
    static readonly MethodInfo s_BuildObjectValue;
    static readonly FieldInfo s_AffectedRows;
    static readonly FieldInfo s_Parameters;
 
    static AcquiringObjectDataSourceView()
    {
        s_Owner = typeof(ObjectDataSourceView).GetField("_owner", NeedlesslyPrivate);
 
        Type[] getType = new Type[] { typeof(string) };
        s_GetType = typeof(ObjectDataSourceView).GetMethod("GetType", NeedlesslyPrivate, null, getType, null);
 
        Type[] tryGetDataObjectType = new Type[] { };
        s_TryGetDataObjectType = typeof(ObjectDataSourceView).GetMethod("TryGetDataObjectType", NeedlesslyPrivate, null, tryGetDataObjectType, null);
 
        Type[] mergeDictionaries = new Type[] { typeof(ParameterCollection), typeof(IDictionary), typeof(IDictionary) };
        s_MergeDictionaries = typeof(ObjectDataSourceView).GetMethod("MergeDictionaries", NeedlesslyPrivateStatic, null, mergeDictionaries, null);
 
        Type[] getResolvedMethodData = new Type[] { typeof(Type), typeof(string), typeof(Type), typeof(object), typeof(object), typeof(DataSourceOperation) };
        s_GetResolvedMethodData = typeof(ObjectDataSourceView).GetMethod("GetResolvedMethodData", NeedlesslyPrivate, null, getResolvedMethodData, null);
 
        Type[] buildObjectValue = new Type[] { typeof(object), typeof(Type), typeof(string)};
        s_BuildObjectValue = typeof(ObjectDataSourceView).GetMethod("BuildObjectValue", NeedlesslyPrivateStatic, null, buildObjectValue, null);
 
        Type objectDataSourceMethod = typeof(ObjectDataSourceView).GetNestedType("ObjectDataSourceMethod", BindingFlags.NonPublic);
        s_Parameters = objectDataSourceMethod.GetField("Parameters", NeedlesslyPrivate);
 
        Type[] invokeMethod = new Type[] { objectDataSourceMethod };
        s_InvokeMethod = typeof(ObjectDataSourceView).GetMethod("InvokeMethod", NeedlesslyPrivate, null, invokeMethod, null);
 
        Type objectDataSourceResult = typeof(ObjectDataSourceView).GetNestedType("ObjectDataSourceResult", BindingFlags.NonPublic);
        s_AffectedRows = objectDataSourceResult.GetField("AffectedRows", NeedlesslyPrivate);
    }
 
    public AcquiringObjectDataSourceView(ObjectDataSource owner, string name, HttpContext context)
        : base(owner, name, context)
    {
    }
 
    protected override int ExecuteDelete(IDictionary keys, IDictionary oldValues)
    {
        if (!this.CanDelete)
        {
            throw new NotSupportedException(ExposedSR.GetString(ExposedSR.DeleteNotSupported, this.Owner.ID));
        }
 
        // we only change the behavior of sources that provide a DataObjectTypeName
        if (String.IsNullOrEmpty(this.DataObjectTypeName))
            return base.ExecuteDelete(keys, oldValues);
 
        Type sourceType = this.GetType(this.TypeName);
        Type dataObjectType = this.TryGetDataObjectType();
        IDictionary deleteParameters = new OrderedDictionary(StringComparer.OrdinalIgnoreCase);
 
        if (this.ConflictDetection == ConflictOptions.CompareAllValues)
        {
            if (oldValues == null || oldValues.Count == 0)
            {
                throw new InvalidOperationException(ExposedSR.GetString(ExposedSR.Pessimistic, ExposedSR.GetString(ExposedSR.Delete), this.Owner.ID, "oldValues"));
            }
 
            MergeDictionaries(this.DeleteParameters, oldValues, deleteParameters);
        }
 
        MergeDictionaries(this.DeleteParameters, keys, deleteParameters);
        object dataObject = this.BuildDataObject(dataObjectType, deleteParameters);
 
        object deleteMethod = this.GetResolvedMethodData(sourceType, this.DeleteMethod, dataObjectType, dataObject, null, DataSourceOperation.Delete);
        IOrderedDictionary parameters = ExtractMethodParameters(deleteMethod);
        ObjectDataSourceMethodEventArgs args = new ObjectDataSourceMethodEventArgs(parameters);
        this.OnDeleting(args);
 
        if (args.Cancel)
        {
            return 0;
        }
 
        object result = this.InvokeMethod(deleteMethod);
 
        this.Owner.InvalidateCache();
        this.OnDataSourceViewChanged(EventArgs.Empty);
        return ExtractAffectedRows(result);
    }
 
    protected override int ExecuteInsert(IDictionary values)
    {
        if (!this.CanInsert)
        {
            throw new NotSupportedException(ExposedSR.GetString(ExposedSR.InsertNotSupported, this.Owner.ID));
        }
 
        // we only change the behavior of sources that provide a DataObjectTypeName
        if (String.IsNullOrEmpty(this.DataObjectTypeName))
            return base.ExecuteInsert(values);
 
        Type sourceType = this.GetType(this.TypeName);
        Type dataObjectType = this.TryGetDataObjectType();
 
        if (values == null || values.Count == 0)
        {
            throw new InvalidOperationException(ExposedSR.GetString(ExposedSR.InsertRequiresValues, this.Owner.ID));
        }
 
        IDictionary insertParameters = new OrderedDictionary(StringComparer.OrdinalIgnoreCase);
        MergeDictionaries(this.InsertParameters, values, insertParameters);
        object dataObject = this.BuildDataObject(dataObjectType, insertParameters);
 
        object insertMethod = this.GetResolvedMethodData(sourceType, this.InsertMethod, dataObjectType, null, dataObject, DataSourceOperation.Insert);
        IOrderedDictionary parameters = ExtractMethodParameters(insertMethod);
        ObjectDataSourceMethodEventArgs args = new ObjectDataSourceMethodEventArgs(parameters);
        this.OnInserting(args);
 
        if (args.Cancel)
        {
            return 0;
        }
 
        object result = this.InvokeMethod(insertMethod);
 
        this.Owner.InvalidateCache();
        this.OnDataSourceViewChanged(EventArgs.Empty);
        return ExtractAffectedRows(result);
    }
 
    protected override int ExecuteUpdate(IDictionary keys, IDictionary values, IDictionary oldValues)
    {
        if (!this.CanUpdate)
        {
            throw new NotSupportedException(ExposedSR.GetString(ExposedSR.UpdateNotSupported, this.Owner.ID));
        }
 
        // we only change the behavior of sources that provide a DataObjectTypeName
        if (String.IsNullOrEmpty(this.DataObjectTypeName))
            return base.ExecuteUpdate(keys, values, oldValues);
 
        object updateMethod;
        Type sourceType = this.GetType(this.TypeName);
        Type dataObjectType = this.TryGetDataObjectType();
 
        if (this.ConflictDetection == ConflictOptions.CompareAllValues)
        {
            if (oldValues == null || oldValues.Count == 0)
            {
                throw new InvalidOperationException(ExposedSR.GetString(ExposedSR.Pessimistic, ExposedSR.GetString(ExposedSR.Update), this.Owner.ID, "oldValues"));
            }
 
            IDictionary oldParameters = new OrderedDictionary(StringComparer.OrdinalIgnoreCase);
            MergeDictionaries(this.UpdateParameters, oldValues, oldParameters);
            MergeDictionaries(this.UpdateParameters, keys, oldParameters);
            object oldDataObject = this.BuildDataObject(dataObjectType, oldParameters);
 
            IDictionary newParameters = new OrderedDictionary(StringComparer.OrdinalIgnoreCase);
            MergeDictionaries(this.UpdateParameters, oldValues, newParameters);
            MergeDictionaries(this.UpdateParameters, keys, newParameters);
            MergeDictionaries(this.UpdateParameters, values, newParameters);
            object newDataObject = this.BuildDataObject(dataObjectType, newParameters);
 
            // if oldDataObject and newDataObject are the same object, this is a bit odd... but since we
            // built them old-then-new, the resulting object has the correct values. In general we aren't
            // going to be running that way.
 
            updateMethod = this.GetResolvedMethodData(sourceType, this.UpdateMethod, dataObjectType, oldDataObject, newDataObject, DataSourceOperation.Update);
        }
        else
        {
            IDictionary updateParameters = new OrderedDictionary(StringComparer.OrdinalIgnoreCase);
            MergeDictionaries(this.UpdateParameters, oldValues, updateParameters);
            MergeDictionaries(this.UpdateParameters, keys, updateParameters);
            MergeDictionaries(this.UpdateParameters, values, updateParameters);
            object dataObject = this.BuildDataObject(dataObjectType, updateParameters);
 
            updateMethod = this.GetResolvedMethodData(sourceType, this.UpdateMethod, dataObjectType, null, dataObject, DataSourceOperation.Update);
        }
 
        IOrderedDictionary parameters = ExtractMethodParameters(updateMethod);
        ObjectDataSourceMethodEventArgs args = new ObjectDataSourceMethodEventArgs(parameters);
        this.OnUpdating(args);
 
        if (args.Cancel)
        {
            return 0;
        }
 
        object result = this.InvokeMethod(updateMethod);
 
        this.Owner.InvalidateCache();
        this.OnDataSourceViewChanged(EventArgs.Empty);
        return ExtractAffectedRows(result);
    }
 
    public virtual object AcquireDataObject(Type dataObjectType, IDictionary inputParameters)
    {
        return Activator.CreateInstance(dataObjectType);
    }
 
    // Oh, if only this was a virtual on ObjectDataSourceView!
    private object BuildDataObject(Type dataObjectType, IDictionary inputParameters)
    {
        object dataObject = AcquireDataObject(dataObjectType, inputParameters);
        PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(dataObjectType);
 
        foreach (DictionaryEntry entry in inputParameters)
        {
            string propertyName = (entry.Key == null) ? string.Empty : entry.Key.ToString();
            PropertyDescriptor descriptor = properties.Find(propertyName, true);
 
            if (descriptor == null)
            {
                throw new InvalidOperationException(ExposedSR.GetString(ExposedSR.DataObjectPropertyNotFound, new object[] { propertyName, this.Owner.ID }));
            }
 
            if (descriptor.IsReadOnly)
            {
                throw new InvalidOperationException(ExposedSR.GetString(ExposedSR.DataObjectPropertyReadOnly, new object[] { propertyName, this.Owner.ID }));
            }
 
            object propertyValue = BuildObjectValue(entry.Value, descriptor.PropertyType, propertyName);
            descriptor.SetValue(dataObject, propertyValue);
        }
 
        return dataObject;
    }
 
    #region Proxies for private methods
    private AcquiringObjectDataSource Owner
    {
        get { return (AcquiringObjectDataSource)s_Owner.GetValue(this); }
    }
 
    private Type GetType(string typeName)
    {
        return (Type)s_GetType.Invoke(this, new object[] { typeName });
    }
 
    private Type TryGetDataObjectType()
    {
        return (Type)s_TryGetDataObjectType.Invoke(this, null);
    }
 
    private static void MergeDictionaries(ParameterCollection reference, IDictionary source, IDictionary destination)
    {
        s_MergeDictionaries.Invoke(null, new object[] { reference, source, destination });
    }
 
    private static object BuildObjectValue(object value, Type destinationType, string paramName)
    {
        return s_BuildObjectValue.Invoke(null, new object[] { value, destinationType, paramName });
    }
 
    private object GetResolvedMethodData(Type type, string methodName, Type dataObjectType
                                         , object oldDataObject, object newDataObject, DataSourceOperation operation)
    {
        return s_GetResolvedMethodData.Invoke(this, new object[] { type, methodName, dataObjectType, oldDataObject, newDataObject, operation });
    }
 
    private IOrderedDictionary ExtractMethodParameters(object method)
    {
        return (IOrderedDictionary)s_Parameters.GetValue(method);
    }
 
    private object InvokeMethod(object method)
    {
        return s_InvokeMethod.Invoke(this, new object[] { method });
    }
 
    private int ExtractAffectedRows(object result)
    {
        return (int)s_AffectedRows.GetValue(result);
    }
    #endregion
}
Once again, I'm doing the reflection work once in a static constructor. In my next (and final?) post on this mess, I'll show you how my DynamicCall stuff makes this code look much nicer and makes it tons type-safer (hmmm... is that something I want to have forever Google-able?). There are a couple of notes.
  1. In all but the insert case, I have a list of keys provided by my caller in a seperate collection. It would be nice to be able to pass that along to the AcquireDataObject method, but it turns out that this isn't necessarily possible or sufficient. Since the ObjectDataSource forwards any explicitly-defined parameters through to the ObjectDataSourceView the key collection given to the update and delete methods may or may not contain the actual key. Rather, the key could be coming from the InsertParameters, DeleteParameters or UpdateParameters collections (which get merged-in in the respective mutation methods).
  2. It would be nice for the AcquireDataObject method to be told why we were trying to acquire the object (e.g. are we doing an insert, update or delete). I didn't make that change in this verison simply to make it easier the correlation to the Microsoft verison, but in the final post, I've added that argument. This allows calling the correct factory method or any other operation-specific behavior.
  3. The calls to this.Owner.InvalidateCache() replaces the bit of class-envy in the original Microsoft version that I talked about in part one.

Recycling messages (or how not to be responsible for translating error messages).

In the continuing saga of my replacement for ObjectDataSource and ObjectDataSourceView, I have had to reimplement some of the methods in those classes. This is done, of course, by using Reflector to see what is currently there and tweaking appropriately. In the process of replacing a method, you probably want to check the same argument conditions and throw the same exceptions as the originals in System.Web.dll. If you can find a way to expose the existing messages and reuse them, you get the added benefit of not having to worry about translation/localization of those error messages. To that end, here's my ExposedSR class that hoists the messages I need from System.Web.dll

internal static class ExposedSR
{
    private static readonly ResourceManager s_Resources = new ResourceManager("System.Web", typeof(ObjectDataSourceView).Assembly);
 
    internal static string GetString(string name)
    {
        return s_Resources.GetString(name, null);
    }
 
    internal static string GetString(string name, params object[] args)
    {
        string text = s_Resources.GetString(name, null);
 
        if (args == null || args.Length == 0)
        {
            return text;
        }
 
        // clip all the string arguments to less than 1K length
        for (int index = 0; index < args.Length; index++)
        {
            string argString = args[index] as string;
 
            if (argString != null && argString.Length > 0x400)
            {
                args[index] = argString.Substring(0, 0x3fd) + "...";
            }
        }
 
        return string.Format(CultureInfo.CurrentCulture, text, args);
    }
 
    internal static readonly string Pessimistic = "ObjectDataSourceView_Pessimistic";
    internal static readonly string InsertNotSupported = "ObjectDataSourceView_InsertNotSupported";
    internal static readonly string UpdateNotSupported = "ObjectDataSourceView_UpdateNotSupported";
    internal static readonly string DeleteNotSupported = "ObjectDataSourceView_DeleteNotSupported";
    internal static readonly string InsertRequiresValues = "ObjectDataSourceView_InsertRequiresValues";
    internal static readonly string Update = "DataSourceView_update";
    internal static readonly string Delete = "DataSourceView_delete";
    internal static readonly string InvalidViewName = "DataSource_InvalidViewName";
    internal static readonly string DataObjectPropertyNotFound = "ObjectDataSourceView_DataObjectPropertyNotFound";
    internal static readonly string DataObjectPropertyReadOnly = "ObjectDataSourceView_DataObjectPropertyReadOnly";
}

The trick here is to create a class that mirrors the normal SR class that is automatically generated by the resources compiler, but coopt it to use the assembly containing the resources you really want. Then readonly strings are there to ease readability when using ExposedSR.

Friday, July 21, 2006

Browser-specific CSS the clearest way possible

How would you like to have all your browser-specific adjustments be trivial to apply? What if you could have a <style> block like this?

<style type="text/css">
.ie .example {
  background-color: yellow
}
.gecko .example {
  background-color: gray
}
.opera .example {
  background-color: green
}
.konqueror .example {
  background-color: blue
}
.safari .example {
  background-color: black
}
.example {
  width: 100px;
  height: 100px;
}
</style>
Now all you need is some JavaScript to apply the correct top-level CSS class to the <html> element. We'll here you go, the CSS Browser Selector from Rafael Lima. Awesome simplicity!

Thursday, July 20, 2006

Enough with the whining, just fix ObjectDataSource already...

What's the deal?

If you have ever tried to use ObjectDataSource with any O/R Mapper or just business entities that have "persistent state", you no-doubt know the issues I've whined about. It really boils down, at the simplest level to the absence of "acquire" semantics for the instance of the DataObjectType specified. In other words, you can just get the "old" instance of the business entity object from your persistence-store / cache / web-service / etc. and then let GridView / DetailsView / FormView update just the properties it has exposed on the user-interface. This is impossible because ObjectDataSourceView simply calls Activator.CreateInstance to create an object ex-nilo. If there was a method you could override on ObjectDataSourceView to acquire the object before all the properties are fiddled-with, then all would be good, but there isn't. I'm going to spend the next few posts talking about how to accomplish that goal. If I'm lucky, this will shame Microsoft into fixing this for the next framework release.

The first hurdle is stupidity design issues.

First, we have to deal with the fact that some things are simply not as they should be in the ObjectDataSource class itself. Firstly, there are a few key fields, properties and types that are private, notably the _view field, the Cache property, and the Enabled property of the SqlDataSourceCache that the Cache stores (not to mention the fact that the SqlDataSourceCache is also private). Secondly, there's a bit of extreme silliness in the implementation of the ,code>GetView call-chain. Like all good DataSourceControls, ODS has an override for the GetView(string viewName) virtual method. This method's body is pretty simple, just verifying you are being a good citizen and passing only the view names that ODS knows about, namely null, String.Empty or "DefaultView", then it just calls the GetView() method to create the actual ObjectDataSourceView instance.

protected override DataSourceView GetView(string viewName)
{
      if ((viewName == null) || ((viewName.Length != 0) && !string.Equals(viewName, "DefaultView", StringComparison.OrdinalIgnoreCase)))
      {
            throw new ArgumentException(SR.GetString("DataSource_InvalidViewName", new object[] { this.ID, "DefaultView" }), "viewName");
      }
      return this.GetView();
}
The GetView() method just does a lazy instantiation against the private ObjectDataSourceView _view field.
private ObjectDataSourceView GetView()
{
      if (this._view == null)
      {
            this._view = new ObjectDataSourceView(this, "DefaultView", this.Context);
            if (base.IsTrackingViewState)
            {
                  ((IStateManager) this._view).TrackViewState();
            }
      }
      return this._view;
}
Here's where things really start to go wrong... the GetView() method is private and non-virtual, so you can't simply override it to return a subclass of ODSV that does what we want. Ah, but that's okay, we can override the GetView(string viewName) virtual method to do what we need, right? Nope, because nowhere in the code of ODS does that method get called. All the standard action methods (like Select(), Delete(), Insert() and Update()) directly call the non-replacable GetView() private method and then directly delegate the action. Ick. Okay, no problem, we just override the action methods, right? Nope, again... they are also non-virtual. Also, there are tons of properties on ODS that do the same direct call to GetView() and delegate the call down, this is especially bad for things like EnablePaging and similar properties, since they get called while we're still constructing the ODS in the cool new declarative coding model of ASP.Net 2.0.

What this basically means is that we're going to have to code up a derived class replacement for ODS and then poke in our replacement for the _view in the private bits. Without further adieu, let me introduce AcquiringObjectDataSource (I've eliminated all the XML documentation, comments and attributes for this post):

public class AcquiringObjectDataSource : ObjectDataSource
{
    const BindingFlags NeedlesslyPrivate = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
 
    static MethodInfo s_InvalidateCacheEntry;
    static MethodInfo s_GetCache;
    static MethodInfo s_GetCacheEnabled;
    static FieldInfo s_View;
 
    static AcquiringObjectDataSource()
    {
        s_InvalidateCacheEntry = typeof(ObjectDataSource).GetMethod("InvalidateCacheEntry", NeedlesslyPrivate, null, new Type[0], null);
        PropertyInfo cache = typeof(ObjectDataSource).GetProperty("Cache", NeedlesslyPrivate);
        s_GetCache = cache.GetGetMethod(true);
        PropertyInfo cacheEnabled = cache.PropertyType.GetProperty("Enabled", NeedlesslyPrivate);
        s_GetCacheEnabled = cacheEnabled.GetGetMethod(true);
        s_View = typeof(ObjectDataSource).GetField("_view", NeedlesslyPrivate);
    }
 
    public AcquiringObjectDataSource()
    {
        // force creation!
        this.GetView();
    }
 
    public AcquiringObjectDataSource(string typeName, string selectMethod)
        : base(typeName, selectMethod)
    {
        // force creation!
        this.GetView();
    }
 
    protected virtual ObjectDataSourceView GetView()
    {
        ObjectDataSourceView view = (ObjectDataSourceView)s_View.GetValue(this);
 
        if (view == null)
        {
            view = new AcquiringObjectDataSourceView(this, "DefaultView", this.Context);
            s_View.SetValue(this, view);
 
            if (base.IsTrackingViewState)
            {
                ((IStateManager)view).TrackViewState();
            }
        }
 
        return view;
    }
 
    protected override DataSourceView GetView(string viewName)
    {
        if (viewName == null || (viewName.Length != 0 && !string.Equals(viewName, "DefaultView", StringComparison.OrdinalIgnoreCase)))
        {
            throw new ArgumentException(ExposedSR.GetString(ExposedSR.InvalidViewName, new object[] { this.ID, "DefaultView" }), "viewName");
        }
 
        return this.GetView();
    }
 
    protected void InvalidateCache()
    {
        object cache = s_GetCache.Invoke(this, null);
        object cacheEnabled = s_GetCacheEnabled.Invoke(cache, null);
 
        if ((bool)cacheEnabled)
        {
            s_InvalidateCacheEntry.Invoke(this, null);
        }
    }
}
Some notes are in order to explain that code:
  1. We force creation of the view during our constructor to insure our GetView() gets a chance to create an instance of our AcquiringObjectDataSourceView before something in ODS causes one to be created. This isn't as lazy as possible, but it's safe.
  2. We have static constructor to do the one-time Reflection to get all the private bits we want to play with. In a later post, I'll show you how to use the DynamicCall stuff in my Utlities library to handle this is a much type-safer and faster way.
  3. We override GetView(string viewName) merely to insure that if someone does call it, we will do the right thing and call our GetView() and not the one in ODS
  4. That last method is there to cure a little class-envy on the part of ODSV, which makes a few calls against the internal SqlDataSourceCache Cache property. They typically look like this:
    if (this._owner.Cache.Enabled)
    {
       this._owner.InvalidateCacheEntry();
    }
  5. The ExposedSR class is just something letting me reuse the localized exception messages embedded in the System.Web.dll assembly, we'll see it in part 3 part 2.

Next time, right here...

Next time In part 3, I'll talk about the issues in ObjectDataSourceView and present the replacement class AcquiringObjectDataSourceView

Wednesday, July 19, 2006

Need a date range in SQL without filling a table?

It's evidentally SQL week here at Chez Brooks. Today I needed a really high performance query to deliver a date range table between two dates. Simple, and there seem to be tons of variants out there, but I didn't like the query plans of any of them. The resulting query below works for any start date and end date pair, and will return a date between two given dates.

DECLARE @LowDate DATETIME
SET @LowDate = '01-01-2006'

DECLARE @HighDate DATETIME
SET @HighDate = '12-31-2016'

SELECT DISTINCT DATEADD(dd, Days.Row, DATEADD(mm, Months.Row, DATEADD(yy, Years.Row, @LowDate))) AS Date
FROM
(SELECT 0 AS Row UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4
 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9
 UNION ALL SELECT 10 UNION ALL SELECT 11 UNION ALL SELECT 12 UNION ALL SELECT 13 UNION ALL SELECT 14
 UNION ALL SELECT 15 UNION ALL SELECT 16 UNION ALL SELECT 17 UNION ALL SELECT 18 UNION ALL SELECT 19
 UNION ALL SELECT 20 -- add more years here...
) AS Years
INNER JOIN
(SELECT 0 AS Row UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4
 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9
 UNION ALL SELECT 10 UNION ALL SELECT 11
) AS Months
ON DATEADD(mm, Months.Row,  DATEADD(yy, Years.Row, @LowDate)) <= @HighDate 
INNER JOIN
(SELECT 0 AS Row UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4
 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9
 UNION ALL SELECT 10 UNION ALL SELECT 11 UNION ALL SELECT 12 UNION ALL SELECT 13 UNION ALL SELECT 14
 UNION ALL SELECT 15 UNION ALL SELECT 16 UNION ALL SELECT 17 UNION ALL SELECT 18 UNION ALL SELECT 19
 UNION ALL SELECT 20 UNION ALL SELECT 21 UNION ALL SELECT 22 UNION ALL SELECT 23 UNION ALL SELECT 24
 UNION ALL SELECT 25 UNION ALL SELECT 26 UNION ALL SELECT 27 UNION ALL SELECT 28 UNION ALL SELECT 29
 UNION ALL SELECT 30
) AS Days
ON DATEADD(dd, Days.Row, DATEADD(mm, Months.Row,  DATEADD(yy, Years.Row, @LowDate))) <= @HighDate
WHERE DATEADD(yy, Years.Row, @LowDate) <= @HighDate
ORDER BY 1

Some notes on this:

  1. If we assume a start date of January 1st, we need to add at most 11 months and at most 30 days to bump to then end of the year, so that's where the 0-11 months and 0-30 days come from.
  2. Due to the fact that some months have less than 31 days, we can conceivably generate the same date twice. By adding one month and 28 days to January 31st we get the same date as adding zero days and two months; either way the result is March 1st. Thus we need the DISTINCT operator.
  3. If you don't care about the dates being in order, delete the ORDER BY clause.
  4. I've currently limited it to a 20 year range, but you can change that in the subquery that generates the years quite easily.
  5. Doing the DATEADD in the order of year, then month, then days is very important as it insure that the correct leap-day rule is followed.

Tuesday, July 18, 2006

More on DATEs and SQL

Building on this First/Last day of month post (oddly, one of the main ways people seem to land on my blog), here are some other commonly needed SQL queries:

Beginning of period

Midnight of any day (i.e. truncate the time from a date)

SELECT DATEADD(dd, DATEDIFF(dd, 0, TheDate), 0)
This works by subtracting the supplied date (like GetDate() for today) from zero--which Microsoft SQL Server interprets as 1900-01-01 00:00:00 and gives the number of days. This value is then re-added to the zero date yielding the same date with the time truncated.

Midnight of today (i.e. what day is today)

SELECT DATEADD(dd, DATEDIFF(dd, 0, GetDate()), 0)
You can also use GetUTCDate() if you are a good developer and are storing everything in UTC.

Monday of any week

SELECT DATEADD(wk, DATEDIFF(wk, 0, TheDate), 0)
This assumes that Sunday is first day of the week. Again, you can use GetDate() or GetUTCDate() for TheDate.

First Day of the Month

SELECT DATEADD(mm, DATEDIFF(mm, 0, TheDate), 0)
This one uses mm to extract the month-only portion of the date just like dd above extracted the date portion.

First Day of the Quarter

SELECT DATEADD(qq, DATEDIFF(qq, 0, TheDate), 0)
Playing the exact same game with quarters yields the expected value.

First Day of the Year

SELECT DATEADD(yy, DATEDIFF(yy, 0, TheDate), 0)
Once more with the yy to extract the year-only portion.

End of period

Okay, so you need the end of the month, quarter, etc. First, remember that if you are not dealing with "known to be date without time" data, you need to be very careful when doing comparisons against a date. For example, comparing a DATETIME column against a user-entered date is almost guaranteed to be wrong if the column has any time component. This is one of the reasons I always prefer to use a BETWEEN clause, as it forces me to think about the date-as-continuum issues. So, almost always, the best thing to do is compare for <. Now that I've justified my reasoning, I'll tell you that it is much easier to get the next "week", "month", "quarter" or "year" and compare for less-than, instead of getting the last value of the current "whatever". Here's the rest:

Midnight of the next day (i.e. truncate the time from date, then get the next)

SELECT DATEADD(dd, DATEDIFF(dd, 0, TheDate) + 1, 0)
note the new + 1. This we get the current date-count, add one and covert it all back (using GetDate or GetUTCDate() should be obvious by now).

Monday of the next week

SELECT DATEADD(wk, DATEDIFF(wk, 0, TheDate) + 1, 0)
Again assumes that Sunday is first day of the week.

First Day of the next Month

SELECT DATEADD(mm, DATEDIFF(mm, 0, TheDate) + 1, 0)

First Day of the next Quarter

SELECT DATEADD(qq, DATEDIFF(qq, 0, TheDate) + 1, 0)

First Day of the next Year

SELECT DATEADD(yy, DATEDIFF(yy, 0, TheDate) + 1, 0)

Putting it to use

This yields queries like this for orders due this month:

SELECT [ID]
FROM [dbo].[Orders]
WHERE [ShipDue] >= DATEADD(mm, DATEDIFF(mm, 0, GetUTCDate()), 0)
AND [ShipDue] < DATEADD(mm, DATEDIFF(mm, 0, GetUTCDate()) + 1, 0)
But wait, Marc... you said you like to use BETWEEN, but that query doesn't have one... that's because BETWEEN is inclusive, meaning it includes the end-points. If I had an Order that was due at midnight of the first day of the next month it would be included. So how do you get the appropriate value for an end-of-period? It's most certainly NOT by using date-parts to assemble one (but is you must, please remember that it's 23:59:59.997 as a maximum time... don't forget the milliseconds). To do it right, we use the incestuous knowledge that Microsoft SQL Server DATETIME columns have at most a 3 millisecond resolution (something that is not going to change). So all we do is subtract 3 milliseconds from any of those end-of-period formulas given above. For example, the last possible instant of yesterday (local time) is:
SELECT DATEADD(ms, -3, DATEADD(dd, DATEDIFF(dd, 0, GetDate()), 0))
So to do the orders due this month as a BETWEEN query, you can use this:
SELECT [ID]
FROM [dbo].[Orders]
WHERE [ShipDue] BETWEEN DATEADD(mm, DATEDIFF(mm, 0, GetUTCDate()), 0)
AND DATEADD(ms, -3, DATEADD(mm, DATEDIFF(mm, 0, GetUTCDate()) + 1, 0))

Remember, always make sure that you do math against input parameters, NOT columns, or you will kill the SARG-ability of the query, which means indexes that might have been used aren't.

Here's the complete pastable list:

SELECT
DATEADD(dd, DATEDIFF(dd, 0, GetDate()), 0) As Today
, DATEADD(wk, DATEDIFF(wk, 0, GetDate()), 0) As ThisWeekStart
, DATEADD(mm, DATEDIFF(mm, 0, GetDate()), 0) As ThisMonthStart
, DATEADD(qq, DATEDIFF(qq, 0, GetDate()), 0) As ThisQuarterStart
, DATEADD(yy, DATEDIFF(yy, 0, GetDate()), 0) As ThisYearStart
, DATEADD(dd, DATEDIFF(dd, 0, GetDate()) + 1, 0) As Tomorrow
, DATEADD(wk, DATEDIFF(wk, 0, GetDate()) + 1, 0) As NextWeekStart
, DATEADD(mm, DATEDIFF(mm, 0, GetDate()) + 1, 0) As NextMonthStart
, DATEADD(qq, DATEDIFF(qq, 0, GetDate()) + 1, 0) As NextQuarterStart
, DATEADD(yy, DATEDIFF(yy, 0, GetDate()) + 1, 0) As NextYearStart
, DATEADD(ms, -3, DATEADD(dd, DATEDIFF(dd, 0, GetDate()) + 1, 0)) As TodayEnd
, DATEADD(ms, -3, DATEADD(wk, DATEDIFF(wk, 0, GetDate()) + 1, 0)) As ThisWeekEnd
, DATEADD(ms, -3, DATEADD(mm, DATEDIFF(mm, 0, GetDate()) + 1, 0)) As ThisMonthEnd
, DATEADD(ms, -3, DATEADD(qq, DATEDIFF(qq, 0, GetDate()) + 1, 0)) As ThisQuarterEnd
, DATEADD(ms, -3, DATEADD(yy, DATEDIFF(yy, 0, GetDate()) + 1, 0)) As ThisYearEnd

For general reading about dates and time, might I suggest this post's links.

Wednesday, July 12, 2006

Driving Directions Syntax Coadified [SIC]

Noah Coad finally comes up with a concise and simple to follow syntax for giving written driving directions. I love it and plan to adopt it post-haste. I'm only changing two things.

  1. I'm putting a slash between the compass-direction and Left-Right indicator (e.g. N/L for "North by turning Left")
  2. I'm using X for Exit, not Ex
So his example with my changes is:
W 520, N/R I405, X S/L I5 A. 4m, X 179 W/L 220th, S/L 70th @ bottom of the hill B. 99 Aurora, * R Which translates to: “Go west on 520, Go north by turning right onto I405, Exit to the south by turning left on I5 after 4 miles, next take exit 179 and head west by turning left onto 220th, then go south by turning left onto 70th which will be at the bottom of the hill before 99 Aurora (in case you go too far), the place will be on your right”

Thanks Noah!

Monday, July 10, 2006

It's all relative... value doubly so...

He did it! In under a year one red paperclip has been traded for a house. It's amazing what a little visions and the Internet can produce... (apologies to Douglas Adams, R.I.P. for the title)

Lower your bandwidth costs using P2P

If you have largish files you want to share, but are concerned with the speed of your upload link or bandwidth costs there are several options:

  • You can post a Torrent file and the consumer can use a Torrent client; I like uTorrent. The downside is that this requires a client-side program be installed by the user.
  • You can use a CDN proxy; I like the free Coral Content Distribution Network. The downside is that it requires port 8080 be open at this time (promises of just-port-80 long pending).
  • You can now use RedSwoosh to proxy the links. This also requires a client-side download, but they serve it up automatically.

Saturday, July 01, 2006

The power of generics compels you, the power of generics compels you...

Once again I've devoted some time to the exorcism of the daemons of Reflection. This time, I'm chasing the cost of calling MethodInfo.Invoke against an arbitrary (chosen at run-time) method.

In previous efforts, I've tried to give you generic sorting with as little cost as possible, added support for Enum properties, improved upon it and added support for access to structs (i.e. ValueTypes) and Nullable<>. Along the way, I learned a lot about IL and the rules for using Reflection.Emit and DynamicMethod for LCG.

Now the cost of calling any arbitrary method of a class isn't quite as apparent as when you're trying to do dynamic comparisons, but it's still significant. Using my new DynamicFunction and DynamicProcedure classes is really noticeable. Running against the same test-jig Person class and Animal struct used in the DynamicComparer sample program, I get the following performance chart executing three methods against 500000 objects:

MethodElapsed time (seconds)Explanation
Compile-time:00.305The test method simply executes the method calls directly with no dynamic choices. This is the baseline for comparison, as good as it can get.
Dynamic Strong:00.591This is called using the strong-typed delegate form, where all the argument types are known and correctly specified. The specific methods called are specified by a string method name.
Dynamic Weak:00.776This is called using the weak-typed delegate form, where all the arguments are passed as a params object[] object, but are of the correct type. The specific methods called are specified by a string method name.
Reflection:18.243This is called using the a standard MethodInfo.Invoke, where all the arguments are passed as a new object[] object, but are of the correct type. The specific methods called are specified by a string method name. This is 31 times slower than the strong-type form!

While building this, I've extensively refactored the logic for the Reflection.Emit into another class. The new DynamicEmit encapsulates the ILGenerator used during the synthesis of the DynamicMethod in both the old DynamicComparer and the new classes.

DynamicFunction

This class allows you to call any method (instance or static) of a class with any return type. There are two supported forms of the delegate.

  • The first form is a weak-typed delegate that takes an params object[] for any arguments needed. This version is much faster than plan old MethodInfo.Invoke, but about 30% slower than the strong-typed delegate form. You instantiate and call the method like this, given a target method of bool YourClass.MethodToCall(int, string):
    YourClass target = new YourClass();
    Func<T, bool> method = DynamicFunction<YourClass, bool>.Initialize('MethodToCall");
    bool result = method(target, 1, "Hi");
    The two arguments are automatically wrapped up by C# in a new object[] and then unwrapped and coerced into the target method's types (if needed). The wrapping and unwrapping is the source of most of the performance difference between this and the strong-type form.
  • The second form is a strong-typed delegate that takes an params object[] for any arguments needed. This version is much faster than plan old MethodInfo.Invoke, but about 50% slower than directly coded calls, but gives you the obvious advantage of variability. You instantiate and call the method like this, given the same target method of bool YourClass.MethodToCall(int, string):
    YourClass target = new YourClass();
    Func<T, bool, int, string> method = DynamicFunction<YourClass, bool, int, string>.Initialize('MethodToCall");
    bool result = method(target, 1, "Hi");
    The two arguments are now passed directly to the delegate, which will be coerced into the target method's types (if needed, not usually).
Note that any type-coercision is only that which can be done by a implicit, so in general you should code the argument types "correctly", plus when the types are the same the code is shorter and faster.

DynamicProcedure

This class allows you to call any method (instance or static) of a class that doesn't return a value (e.g. it's a void method). There are two supported forms of the delegate.

  • The first form is a weak-typed delegate that takes an params object[] for any arguments needed. This version is much faster than plan old MethodInfo.Invoke, but about 30% slower than the strong-typed delegate form. You instantiate and call the method like this, given a target method of void YourClass.VoidMethodToCall(int, string):
    YourClass target = new YourClass();
    Proc<T> method = DynamicProcedure<YourClass>.Initialize('VoidMethodToCall");
    method(target, 1, "Hi");
    The two arguments are automatically wrapped up by C# in a new object[] and then unwrapped and coerced into the target method's types (if needed). The wrapping and unwrapping is the source of most of the performance difference between this and the strong-type form.
  • The second form is a strong-typed delegate that takes an params object[] for any arguments needed. This version is much faster than plan old MethodInfo.Invoke, but about 50% slower than directly coded calls, but gives you the obvious advantage of variability. You instantiate and call the method like this, given the same target method of void YourClass.VoidMethodToCall(int, string):
    YourClass target = new YourClass();
    Proc<T, bool, int, string> method = DynamicProcedure<YourClass, int, string>.Initialize('MethodToCall");
    bool result = method(target, 1, "Hi");
    The two arguments are now passed directly to the delegate, which will be coerced into the target method's types (if needed, not usually).
Note that any type-coercision is only that which can be done by a implicit, so in general you should code the argument types "correctly", plus when the types are the same the code is shorter and faster.

What does the code look like?

class Person
{
    public Person(string name) { ... }
    public bool Compatible(Person potentialMate) { ... }
    public Person Breed(Person mate, Gender childGender) { ... }
    public void Mutate() { ... }
}
 
Func<Person, bool, Person> compatible = DynamicFunction<Person, bool, Person>.Initialize("Compatible");
Func<Person, Person, Person, Gender> breed = DynamicFunction<Person, Person, Person,  Gender>.Initialize("Breed");
Proc<Person> mutate = DynamicProcedure<Person>.Initialize("Mutate");
 
Person child;
 
if (compatible(new Person("Marc"), new Person("Beth"))
    child = breed(you, me, Gender.Female);
else
    mutate(me);

What does the emitted IL code look like?

A sample of the emitted code, in weak-typed form call for the above bool Compatible(Person) looks like this:

IL_0000: /* 03  |          */ ldarg.1    
IL_0001: /* 16  |          */ ldc.i4.0   
IL_0002: /* 9a  |          */ ldelem.ref 
IL_0003: /* 74  | 02000002 */ castclass  DynamicComparerSample.Person
IL_0008: /* 0a  |          */ stloc.0    
IL_0009: /* 02  |          */ ldarg.0    
IL_000a: /* 06  |          */ ldloc.0    
IL_000b: /* 28  | 0A000003 */ call       Boolean Compatible(DynamicComparerSample.Person)/DynamicComparerSample.Person
IL_0010: /* 2a  |          */ ret

A sample of the (much simpler) emitted code, in strong-typed form call for the above bool Compatible(Person) looks like this:

IL_0000: /* 02  |          */ ldarg.0    
IL_0001: /* 03  |          */ ldarg.1    
IL_0002: /* 28  | 0A000002 */ call       Boolean Compatible(DynamicComparerSample.Person)/DynamicComparerSample.Person
IL_0007: /* 2a  |          */ ret

As always, download here