Introduction
If you had the chance to work with LINQ, the latest Object Relational Mapping tool (ORM) added to the .NET 3.5 platform, then you understand how LINQ maps your database structure into several classes in your application. It creates for each table an entity class that has its properties mapped to the columns of the table in context. The whole point of ORM tools is to bring the database structure up to the language’s level so that your code would be aware of the structure at compile time. Nowadays developers tend to divide their application’s architecture into several distinct layers, usually 3 layers:
- Physical layer which encapsulates code that accesses your database one way or another
- Business layer which usually has your business logic code
- User interface layer which is usually a desktop .NET application or an ASP.NET application
I am not going to go into the details of the benefits of dividing your design into multi layers, nor discuss the pros and cons of LINQ itself, there are a lot of article on the Internet on this subject.
When you use LINQ in your physical layer to access the database, LINQ creates entity objects. A quick example will explain this. If you have a table called Accounts
then LINQ creates a class called Account
that has its properties set similar to the columns in the database. Now if you use this class itself to transfer the data to the upper layers (specifically all the way to the presentation layer) then you would realize that your application will cease to be multi-tier in dependencies. Your presentation layer will “see” entity classes that are created by the physical layer and this is bad because your layers are no longer loosely coupled as one would hope, but they are tightly integrated with each other.
To keep the spirit of multi-tier architecture, developers usually create a set of parallel classes that map the entity classes, by doing this, we add a layer of abstraction necessary to keep things clean between the dependencies between layers. This way, the presentation layer would only depend on the business layer and no longer on any layer below that.
Usually there are two approaches to these intermediate classes, also known as data transfer objects, dtos. Somewhere along your code, you will need to transfer the data between the entity objects and between your data transfer objects. So how do we do this? Some choose to code this part manually which is good, performance wise, but big trouble when it comes to maintaining these classes, not to mention all the amount of wasted human energy in donkey work. Some choose to benefit from the powerful features of reflection and write some code to copy the objects at runtime based on their properties, this is usually the preferred approach. The properties of the objects are discovered at runtime using Reflection, and copied to each other. This method has the big advantage that you do not need to write code for each pair of classes to copy properties from the entity classes to the data transfer objects, this saves you a lot of maintenance and a lot of time. Unfortunately this method suffers from low performance as reflection is slow, and you will not know how low the performance is until you really test it.
The Solution, A Better Approach
What if you can take the advantages of these two methods and none of their disadvantages? What if you choose to go with the first approach, where you write code for each and every pair of classes to copy their properties but instead of writing them yourself, you would have a developer working for you at runtime and writing this code for you very quickly and best of all, he works for free. This programmer is Mr. Emit.
Mr. Emit or his full name is System.Reflection.Emit
will use Reflection
at runtime to discover the two objects, the entity and the dto and then write code for you once that you can use at runtime to copy the properties efficiently between the two objects. Reflection.Emit
is the namespace
that allows you to create code at runtime using IL language, the intermediate language of the .NET platform.
Using the Code
Using this code is very simple, just add it to your solution or add a reference to its DLL. And whenever you have two objects to copy, just pass them to the static
function DynamicFiller.FillObject(…)
or use DynamicFiller.FillArray
if your input is an array of objects and not just one object. There is a method in this class called AllowedType
. This method has the types of properties that this class is allowed to copy, you can add to it whenever you have some types of properties that are not handled.
Performance Benefits
Before I wrote this code, I had code that used reflection. Using reflection, copying 10,000 objects (with around 15 properties for the object’s class) took around 32 seconds on a 1.8 Mhz Core 2 Duo Centrino CPU with 2 GB of RAM Laptop (non debug), in debug mode it took 1 minute, 19 seconds. Doing the same operation using the new Emit code took 19 milliseconds in non debug and 22 milliseconds in debug mode. Yes that is correct, milliseconds. The numbers speak for themselves.
A Look Under the Hood
Explaining how to write IL (Intermediate Language) code is beyond the scope of this article, however I will try to give you a head start if you are interested in understanding what the code does and how it does it. The code creates classes at runtime that implement the interface IDynamicFiller
. The IDynamicFiller
has just one method that is called FillObject
and takes two objects of type Object
that the class should copy. When these classes are coded at runtime, they are given a composite name that depends on the type of objects passed in and they are added to a Dictionary, this way the same class will be retrieved the next time it is needed to copy data between the same two types of objects. The created classes implement the interface IDynamicFiller
by creating a new method class FillObject
and inside this method the two passed objects are casted to their proper types and then properties are copied from one object to the other.
Conclusion
System.Reflection.Emit
is an underused namespace
due to the fact that you would need to know IL to use it. However it is not that hard, if you give it sometime the rewards are worth your while. A lot of solutions developed using the Reflection namespace
can be made much faster using Emit namespace
. And this is a very good example. Happy coding.
History
- 25th April, 2008: Initial post
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.