In this article, I am going to take you through a scenario where you can use dynamically created assemblies in your software solutions. I will be focusing more on
System.CodeDom namespace, but towards the end I will discuss the use of
System.Reflection.Emit namespace that helps you to create dynamic assemblies.
Consider business scenarios were due to market trends and other effects you decide to give your clients a special offer. This can change according to the time of the year, type of the item, etc. In addition, you will decide to give discounts for a certain selected item category according to the quantity or the total price. Furthermore, you want to change the validation rules of your application. For instance, you want to restrict the quantity of some item category that can be bought remotely.
Imagine that your system architecture is where your clients log in to your server using Windows application, using a socket or Remoting facilities provided in .NET Framework.
When you look at this problem, you will see that you cannot hard code these requirements into your client application. It’s purely because parameters and equations are decided by the server itself according to the market trends and to maximize the profit margins. So your client application must have the capability to be configured by the server. In addition in this type of architecture, you have to reduce the network communication and utilization of bandwidth unnecessarily. To tackle this problem, you can use the capabilities of the
System.CodeDom namespace. The main feature provided by this technique is to generate source code at runtime in multiple languages. Then
System.CodeDom.Compiler will give you freedom to compile the source code you dynamically created with relevant language compiler into a class library or an executable.
In my demo application, I am going to dynamically create an assembly and use it for discount calculations, special offer calculations and for some validations. Here I am going to consider a situation of a book shop, where there are a set of product categories such as Books, Magazines, Gifts and Stationary. There could be sub product categories as well. Below I have mentioned some of the rules and offers I am going to implement in this application.
Book category - “B”
- 2% discount if the total value exceeds $50
- 3.5% discount if the total value exceeds $150
- 5.75% discount if the total value exceeds $400
- 6% for first 500 and $5 discount for each $100 above 500.
Gift Category – “G”
- Maximum order value that can be ordered online is 1000$
Magazine category - “M” and sub category Fashion – “F”
- Maximum quantity that can be ordered online is 2
- There is 2.5% discount if the total value exceeds $100
Magazine category - “M” and sub category Technical – “T”
- If order value is over 750, there is a choice to select magazine from a set of magazines populated in the order window.
Stationary category - “S”
- If the order value is over 500$ there is a choice to select a free gift from a set of gifts populated in the order window.
As I clearly mentioned, these values and the parameters will be changed according to the market trends and profit margins to be achieved.
Even though I have used a Rules.xml XML document to store my dynamic rules sent from the sever, in real circumstances these rules are sent from the server as
strings and they will change according to requirements. If you inspect the XML, you can see that in the body element what you have is C# code. Similarly
ReturnType element is C# type. Imagine that Rule.xml is sent from the server or similar set of data is sent from the server. Then the next step is to include this in a C# class and create an assembly on the fly.
First let’s concentrate on how to create a class using the code segments sent from the server.
CodeCompileUnit is the main object that maintains or models the structure of the source we are going to write also known as the CodeDOM graph of a source. Using this object, we can add specific namespaces we want, to import the referenced namespaces used in the source and define the types such as classes that will be declared inside that program structure. There are a set of classes in the
System.Codedom namespace that is referred by this
CodeCompileUnit class. Please go through the following code segment.
CodeNamespace codeNamespace = new CodeNamespace("DynamicAssemblyDemo.DynamicAssembly");
bookShopRuleClass = new CodeTypeDeclaration("BookShopRuleClass");
In the above code segment, you can see that I have used
CodeNamespace class to declare the namespace of the dynamic class I am going to create (
DynamicAssemblyDemo.DynamicAssembly). Then I have imported
System namespace which will be used in this class. The other important class that I have used here is
CodeTypeDeclaration which represents a type declaration for a class, structure, etc. The name of the dynamic class is
CodeTypeDeclaration object, you can find a set of interesting properties to shape your type, in my case the class just like a normal class. For instance, you can add the comment to code, break the code into regions, you can add member fields and methods. I have used the
Member property of this class extensively to add local fields and methods.
CodeTypeMemberCollection is a collection of
CodeTypeMember type which is the base class of many important types that can be added as members to a class. In the following table extracted from the MSDN, you can see those classes.
System.CodeDom.CodeMemberEvent: Represents a declaration for an event of a type
System.CodeDom.CodeMemberField: Represents a declaration for a field of a type
System.CodeDom.CodeMemberMethod: Represents a declaration for a method of a type
System.CodeDom.CodeMemberProperty: Represents a declaration for a property of a type
System.CodeDom.CodeSnippetTypeMember: Represents a member of a type using a literal code fragment
System.CodeDom.CodeTypeDeclaration: Represents a type declaration for a class, structure, interface, or enumeration
In this implementation, I have used
CodeMemberMethod types to setup the class structure I want. Now let’s move in to the
CodeMemberField GetFieldCode(string name,CodeTypeReference fieldType)
CodeMemberField field = new CodeMemberField();
field.Name = name;
field.Attributes = MemberAttributes.Public;
field.Type = fieldType;
The above code is straightforward. Here we create a field that will be added to the class we created. This
CodeMemberField object is added to the
Member collection of the
bookShopRuleClass. Here is the sample code:
CodeMemberField tempField = GetFieldCode("category",
Similarly I have added the methods that will be called from ultimately for validations and special offer calculations.
CodeMemberMethod SetMethod(string name,CodeTypeReference returnType,string methodBody)
CodeMemberMethod method = new CodeMemberMethod();
method.Name = name;
CodeSnippetStatement statement = new CodeSnippetStatement(methodBody);
method.ReturnType = returnType;
method.Attributes = MemberAttributes.Public;
Main things you have to keep in mind are that rules you have to adhere to are the same as the normal class. You cannot have the same method signatures duplicated everywhere or have incorrect syntax inside the method bodies. To avoid the duplication of the same signature within the class, I have used the category code integrated with the method name. For example to
GetDiscount_M() method signatures, avoid the duplication of the
GetDiscount method inside the same class. In the above code
CodeSnippetStatement class helps us a lot to insert a literal code segment into the source code without any modifications.
BookShopRuleClass implementation is done. The whole implantation is inserted into the
CodeCompileUnit object. The next step is to compile the code. For this vital step, we have to use the
System.CodeDom.Compiler namespace. Given the code structure or
CodeCompileUnit/s we can compile it using an appropriate compiler and generate assembly file or an executable. Here we have to use some derived class of
CodeDomProvider to compile the structure. In this case, we have to use the
CSharpCodeProvider class to compile our
BookShopRuleClass. In this implementation, I used
CompileAssemblyFromDom methods respectively to generate the source code and to get the compiled assembly. Apart from the
CompileAssemblyFromDom method, there are two other methods to compile a code, namely
CompileAssemblyFromSource. I think the method says where you can use these methods.
After creating this assembly, it’s all Reflection afterwards. In the
RuleManager class, using that assembly I have created a
BookShopRuleClass instance and using reflection invoking the specific method accordingly. I will not go into details of
RuleManager class since it’s not in the scope of this article. If you have some knowledge in Refection, you can easily understand the implementation there.
For the sake of completeness, I will talk about
System.Reflection.Emit namespace very little which is another way of creating dynamic assemblies. However I must say that this is a very hectic and time consuming way. Also for the above scenario, this technique is a bit difficult to use. Even a simple mistake will cause the assembly creation process to fail. Please go through the code segment below. Before that, it is somewhat important to understand the opcodes and other syntax used in MSIL (Microsoft intermediate language). Also to get familiarized with this syntax, use the MSIL Disassembler (Ildasm.exe) tool and go through some of the Assemblies you created.
public Assembly CreatAssembly()
AssemblyBuilder ab = null;
AssemblyName an = new AssemblyName();
an.Version = new Version(1, 0, 0, 0);
an.Name = "BookShopRuleWithEmit";
ab = Thread.GetDomain().DefineDynamicAssembly(an, AssemblyBuilderAccess.RunAndSave);
ModuleBuilder modBuilder = ab.DefineDynamicModule("BookShopRuleWithEmit",
TypeBuilder tb = modBuilder.DefineType("BookShopRuleWithEmit.BookShopRules",
FieldBuilder price = tb.DefineField("price", typeof(double), FieldAttributes.Public);
FieldBuilder qty = tb.DefineField("quantity", typeof(double), FieldAttributes.Public);
MethodBuilder adderBldr = tb.DefineMethod("GetDiscount",
ILGenerator ilgen = adderBldr.GetILGenerator();
Label failed = ilgen.DefineLabel();
Label failed2 = ilgen.DefineLabel();
Label endOfMthd = ilgen.DefineLabel();
catch (Exception e)
Now you can see the value of the
System.CodeDom namespace. Using dynamically generated assembly, we can reduce the server client conversations immensely, hence optimizing the network usage as well as reducing the sever load.
- Mr. Rohan Mapatuna
- Mr. Uditha Bandara
- 24th August, 2008: Initial post