Sunday, October 31, 2010

Using IronRuby at Work: Part 2 – Using AssemblyBuilder and System.Reflection.Emit to create a .NET Assembly.

I will be giving a talk on this subject at this subject on November 6, 2010 at the Triangle .NET User Group’s RDU Code Camp

This is the second post in a three part series in which I will discuss how I used IronRuby to make my life easier.  In this post I’m going to discuss how IronRuby was used to generate a .NET Assembly.

All source files can be downloaded from here

Generating Non-EF Models

Now that I can make queries against the database I want to make non-EF models that I can use to move data from the data access layer to the presentation layer.  Some may think this is over-kill but I like to keep EF out of my presentation layer.  Plus, if I can generate these models it doesn’t take much to have this extra layer.

The code generation process is managed by a class called CodeGen.  The CodeGen class has methods to generate the models and the domain classes.  CodeGen’s initialize method (ruby class’ constructor) there are three important objects created: GenerateAssembly, ModelGenerator and ElementsService.  The GenerateAssembly class is where the actual assembly is created.  The  initialize method creates the dynamic assembly and a module are created with these two calls:

The first call creates the dynamic assembly by passing in the assembly name and version information which is stored in the @asm variable.  The AssemblyBuilderAccess.RunAndSave is used to indicate that we want the ability to run the code as well as save it to the file system. The second call creates the module for the assembly.  I will use the @mod_builder object to create our model classes for our assembly.

After I have a CodeGen object I will call the create_models method.

The create_models method uses the ElementsService class (discussed in the first post of this series), to retrieve all active views using LINQ in my IronRuby script.  For each view returned a call to create_model is made.

The create_model method is where we actually start generating CIL code.  The @model_gen.generate method drives the actual model class creation and adds it to the assembly.  It returns the actual CLR type of the new model and an array that contains the names of all domain level classes that will be used to drive the domain class generation process.  Let’s get into the actual generation of the model classes.

The ModelGenerator class

The ModelGenerator.generate method is called with two parameters.  The first parameter is the name of the class to create and the second one is an array of properties to create.  

The first line of code starts the generation by calling GenerateAssembly.define_class method which returns a TypeBuilder object that represents the class.  The GenerateAssembly.define_class method looks like this:

The define_class parameters are pretty straight forward.  The first parameter is the name of the class to be created.  The second parameter contains the name of the interface the class is going to implement.  If no name is given it will be set to nil (null in C#). The @mod_builder variable is a ModuleBuilder object which I will use to create the model class.  Calling the DefineType method creates a representation of the model class which is returned in a TypeBuilder object that we will be used to create the class’ properties. 

When the define_class call returns we will create the properties.  Creating properties is a little more involved then creating a class.  The properties will be created using the ModelGenerator.create_property method.  The create_property method takes the TypeBuilder object that represents the class, the name of the property and the CLR type of the property.  The last parameter is optional and I will go with the default.

The first line creates the private field that will store the value of the property using the TypeBuilder to call the DefineField method.  I keep the returned FieldBuilder object so I can use it in the body of the setter/getter methods. 

Next I will create the property by calling TypeBuilder.DefineProperty.  The parameters are the name of the property, attributes that describe the property, the CLR type and the parameter types.  Since I do not have parameters in our properties I set it to nil.  The returned PropertyBuilder is stored so I can use it to associate the getter/setter methods to it.

The next step is to create the getter method by calling TypeBuilder.DefineMethod object.  After the DefineMethod call I can start generating CIL, also known as MSIL.  The DefineMethod call returns a MethodBuilder object that we use to get an ILGenerator object which is how I will create the body of the getter method.  The generation is done by making calls to the Emit method.

The first Emit call loads the argument at index one onto the stack.  The next line finds the value of a field in the object whose reference is currently on the evaluation stack, in this case it is the private field that I created first.  The last line adds the return statement to the getter.  As you can see there isn’t much to setting up the getter method.

The process for the setter is similar to the getter except for two things.  The first difference is setting up the parameters for the setter.  The second is to set the value of the private field.  The parameters for the setter are created by these lines. 

Since I am using IronRuby I had to create a generic list of the type Type and add the type parameter to the list. There may be another way to do it but I don’t know any other way to get a CLR array of Type.  Just prior to calling the DefineMethod I convert the list to an array using the .NET generic list’s ToArray method and pass it to the the DefineMethod as the last parameter.  This sets the setter method’s parameter type.

The second difference is the section of code that store the parameter’s value in the private field.  The Ldarg_1 line loads the parameter onto the stack.  The next line sets the value our private field to the value of the parameter.  All the rest of the code for the setter is the same as the getter method.

Finally I relate the getter and setter methods to the property by making these two calls below.

Back to ModelGenerater.generate

After creating all the properties for the model the next thing we need to do is to create the Type for the model’s class. Which is done by making the GetType call below.

Once the GetType method is called I am done with the creation of the model’s class.  After all of the models are created the script writes the new assembly out to the file system using the AssemblyBuilder object that was created in the first line of the CodeGen class’ initialize method.

Summary

After reading the information from the database I was able to convert that data into a model classes in CIL without much work.   I can now take information from 3 database tables and generate models for the classes we need in a few seconds.  Now that I have the generated models and EF entities I am almost ready to start using the data.  In Part 3 of this series I will go over how I create the domain layer classes using the CodeDom.

You can download the IronRuby and C# code for this series from here.

No comments:

Post a Comment