Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I attach a method to a dynamically-created C# type at runtime?

I have been saddled with using an in-house data access library that is effectively XML passed to a stored procedure, which returns XML. There is nothing I can do about this. I tried to get ActiveRecord approved, but my request was declined. However, using the excellent code provided at http://blog.bodurov.com/Post.aspx?postID=27, I added an extension method to IEnumerable that converts the key-value pairs I make out of the ragged XML coming back into strongly typed objects, complete with property names!

This:

dict["keyName1"]

becomes

MyObject.keyName1

Now the interface supports databinding! Pretty cool! I'd like to take it a step further, though. I want the objects emitted to have Save() methods too, so that I can ape the ActiveRecord pattern and provide my web guys with an intuitive object layer to use from ASP.net.

How do I write a method in Visual Studio, in source code, and attach it at runtime to the emitted objects? I am not interested in (or qualified for) writing assembly or IL. I'd like to do this in C#. This is my first StackOverflow question and I am posting this with company-mandated IE6, so please be gentle.

like image 660
Chris McCall Avatar asked Apr 02 '09 15:04

Chris McCall


2 Answers

From what I gather on that article, it's creating anonymous types for you, and you're using that to get the values. If that's the case, there's no easy way to add methods on to those objects. However, if the XML structure will be the same every time the SP executes, why not create a concrete class that has all the properties you need, and populate a collection of those objects yourself with the XML. That way, you can easily add any methods you need directly into the class...

EDIT: Based on our discussion in the comments, here's a thought:

In the code there, when you're building up the type, you're using: ModuleBuilder.DefineType. There's an overload to DefineType which takes a type to extend. Link.. Therefore, create an interface (it doesn't event have to have any methods in it), and when you're dynamically building up the type, extend that interface using the overload I linked you to. Then create an extension method on that interface that does the Save().

There's another overload that may be of interest that takes a type to extend, and interfaces:

http://msdn.microsoft.com/en-us/library/f53tx4x8.aspx

EDIT2: Code sample:

First, create an interface:

public interface ISaveExtentable //I suck at naming stuff :-p
{

}

Then, in the code you liked to in that site, you'll find a method called: GetTypeBuilder. Change it to this:

        private static TypeBuilder GetTypeBuilder(string typeSigniture)
        {
            AssemblyName an = new AssemblyName("TempAssembly" + typeSigniture);
            AssemblyBuilder assemblyBuilder =
                AppDomain.CurrentDomain.DefineDynamicAssembly(
                    an, AssemblyBuilderAccess.Run);
            ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");

            TypeBuilder tb = moduleBuilder.DefineType("TempType" + typeSigniture
                                , TypeAttributes.Public |
                                TypeAttributes.Class |
                                TypeAttributes.AutoClass |
                                TypeAttributes.AnsiClass |
                                TypeAttributes.BeforeFieldInit |
                                TypeAttributes.AutoLayout
                                , typeof(object), new Type[] {typeof(ISaveExtentable)});
            return tb;
        }

Then, create an extension method on that interface to do the save:

    public static class SaveExtendableExtensions
    {
          public static void Save(this ISaveExtentable ise)
          {
              //implement save functionality. 
          }
    }

You'll most likely need to use reflection in your Save method to get all the properties since the type was created dynamically.

like image 96
BFree Avatar answered Nov 04 '22 07:11

BFree


I think this is called mix-ins which aren't directly or easily available in .net. However as I understand it this is one of the main reasons the LinFu framework was designed.

like image 23
Maslow Avatar answered Nov 04 '22 06:11

Maslow