Writing object to object mapper: first implementations

I wrote some object to object mapping code and introduced it in some of my previous postings about performance. As I received 22x performance raise when trying out different mapping methods and it is now time to make my code nice. In this posting I will show you how I organized my code to classes. Yes, you can use my classes in your work if you like. :)

In this posting I will create base class for my O/O-mappers. Also I will provide three implementations of O/O-mapper. There will be reflection based, dynamic code based and IL-instructions based implementations.

NB! Although implementations given here work pretty nice they are very general and therefore not maximally optimal. I will provide faster implementations in my next posting about object to object mapper.

Creating mapper base

I wrote simple base class that provides base type and some common functionality to mapper implementations. Common functionality comes in two methods: GetMapKey() and GetMatchingProperties(). First one of them creates unique string key for two mapped types. Second one queries source and target types and decides what properties are matching. These methods are protected because there is no reason for external code to call them. If you write your own implementations you can override these methods is you like.

public class PropertyMap
{
   
public PropertyInfo SourceProperty { get; set
; }
   
public PropertyInfo TargetProperty { get; set
; }
}
 

public abstract class ObjectCopyBase
{
 
   
public abstract void MapTypes(Type source, Type
target);
   
public abstract void Copy(object source, object
target);
 
   
protected virtual IList<PropertyMap
> GetMatchingProperties
        (
Type sourceType, Type
targetType)
    {
       
var
sourceProperties = sourceType.GetProperties();
       
var
targetProperties = targetType.GetProperties();
 
       
var properties = (from s in
sourceProperties
                         
from t in
targetProperties
                         
where
s.Name == t.Name &&
                                s.CanRead &&
                                t.CanWrite &&
                                s.PropertyType == t.PropertyType
                         
select new PropertyMap
                          {
                              SourceProperty = s,
                              TargetProperty = t
                          }).ToList();
       
return
properties;
    }
 
   
protected virtual string GetMapKey(Type sourceType, Type
targetType)
    {
       
var keyName = "Copy_"
;
        keyName += sourceType.FullName.Replace(
".", "_"
);
        keyName +=
"_"
;
        keyName += targetType.FullName.Replace(
".", "_"
);
 
       
return keyName;
    }
}

Properties query given here is primitive one. My own query is far more complex and has many more conditions. I don’t want to drive your attention away from main topic and that’s why I am using simple implementation of query here. Querying result is property map for two given types. Every property of source type that fits is matched to property of target type.

ObjectCopyReflection – reflection based implementation

My first implementation was based on reflection. It made heavy use of it and was not as optimal as I wanted. Of course, it has also bright side – it is simplest one to read and understand. Just open Google if you are not familiar with reflection and it takes you couple of minutes to understand how this implementation works.

public class ObjectCopyReflection : ObjectCopyBase
{
   
private readonly Dictionary<string, PropertyMap
[]> _maps =
       
new Dictionary<string, PropertyMap
[]>();
 
   
public override void MapTypes(Type source, Type
target)
    {
       
if (source == null || target == null
)
           
return
;
 
       
var
key = GetMapKey(source, target);
       
if
(_maps.ContainsKey(key))
           
return
;
 
       
var
props = GetMatchingProperties(source, target);
        _maps.Add(key, props.ToArray());
    }
 
   
public override void Copy(object source, object
target)
    {
       
if (source == null || target == null
)
           
return
;
 
       
var
sourceType = source.GetType();
       
var
targetType = target.GetType();
 
       
var
key = GetMapKey(sourceType, targetType);
       
if
(!_maps.ContainsKey(key))
            MapTypes(sourceType, targetType);
 
       
var
propMap = _maps[key];
 
       
for (var
i = 0; i < propMap.Length; i++)
        {
           
var
prop = propMap[i];
           
var
sourceValue = prop.SourceProperty.GetValue(source,
                             
null
);
            prop.TargetProperty.SetValue(target, sourceValue,
                                        
null);
        }
    }
}

If you look at Copy() method you can see that this method is pretty safe – it checks if mapping is for types already there and if it is not it creates it. This method was not perfect one because in my test environment it took about 0.0238 ms to copy matching properties from one object to another. Using this class I got the following result: 0,0240 ms.

ObjectCopyDynamicCode – dynamically compiled C#

Next implementation was remarkably faster. I used dynamically generated C# code and it gave me 0,0055 ms as result. Implementation for dynamically compiled C# mapper is here.

public class ObjectCopyDynamicCode : ObjectCopyBase
{
   
private readonly Dictionary<string, Type
> _comp =
       
new Dictionary<string, Type
>();
 
   
public override void MapTypes(Type source, Type
target)
    {
       
if (source == null || target == null
)
           
return
;
 
       
var
key = GetMapKey(source, target);
       
if
(_comp.ContainsKey(key))
           
return
;
 
       
var builder = new StringBuilder
();
        builder.Append(
"namespace Copy {\r\n"
);
        builder.Append(
"    public class "
);
        builder.Append(key);
        builder.Append(
" {\r\n"
);
        builder.Append(
"        public static void CopyProps("
);
        builder.Append(source.FullName);
        builder.Append(
" source, "
);
        builder.Append(target.FullName);
        builder.Append(
" target) {\r\n"
);
 
       
var
map = GetMatchingProperties(source, target);
       
foreach (var item in
map)
        {
            builder.Append(
"            target."
);
            builder.Append(item.TargetProperty.Name);
            builder.Append(
" = "
);
            builder.Append(
"source."
);
            builder.Append(item.SourceProperty.Name);
            builder.Append(
";\r\n"
);
        }
 
        builder.Append(
"        }\r\n   }\r\n}"
);
 
       
// Write out method body
        Debug
.WriteLine(builder.ToString());
 
       
var myCodeProvider = new
CSharpCodeProvider();
       
var
myCodeCompiler = myCodeProvider.CreateCompiler();
       
var myCompilerParameters = new
CompilerParameters();
        myCompilerParameters.ReferencedAssemblies.Add(
           
typeof
(LinqReflectionPerf).Assembly.Location);
        myCompilerParameters.GenerateInMemory =
true
;
       
var
results = myCodeCompiler.CompileAssemblyFromSource
                      (myCompilerParameters, builder.ToString());
 
       
// Compiler output
        foreach (var line in
results.Output)
           
Debug
.WriteLine(line);
 
       
var
copierType = results.CompiledAssembly.GetType("Copy." + key);
        _comp.Add(key, copierType);
    }
 
   
public override void Copy(object source, object
target)
    {
       
if (source == null || target == null
)
           
return
;
 
       
var
sourceType = source.GetType();
       
var
targetType = target.GetType();
 
       
var
key = GetMapKey(sourceType, targetType);
       
if
(!_comp.ContainsKey(key))
            MapTypes(sourceType, targetType);
 
       
var flags = BindingFlags.Public | BindingFlags
.Static |
                   
BindingFlags
.InvokeMethod;
       
var args = new
[] { source, target };
        _comp[key].InvokeMember(
"CopyProps", flags, null, null, args);
    }
}

This implementation is way better than previous one. It takes 0,0058 ms per copying operation. The code is not so simple to read and understand but we got better performance.

ObjectCopyLcg – dynamically generated IL-code

Our last implementation is based on LCG (Lightweight Code Generation) and this code has best performance as you saw from the chart given in LCG posting. Here is the code of LCG implementation of object copy class.

public class ObjectCopyLcg : ObjectCopyBase
{
   
private readonly Dictionary<string, DynamicMethod
> _del =
       
new Dictionary<string, DynamicMethod
>();
 
   
public override void MapTypes(Type source, Type
target)
    {
       
if (source == null || target == null
)
           
return
;
 
       
var
key = GetMapKey(source, target);
       
if
(_del.ContainsKey(key))
           
return
;
 
       
var args = new
[] { source, target };
       
var mod = typeof
(Program).Module;
 
       
var dm = new DynamicMethod(key, null
, args, mod);
       
var
il = dm.GetILGenerator();
       
var
maps = GetMatchingProperties(source, target);
 
       
foreach (var map in
maps)
        {
            il.Emit(
OpCodes
.Ldarg_1);
            il.Emit(
OpCodes
.Ldarg_0);
            il.EmitCall(
OpCodes
.Callvirt,
                        map.SourceProperty.GetGetMethod(),
null
);
            il.EmitCall(
OpCodes
.Callvirt,
                        map.TargetProperty.GetSetMethod(),
null
);
        }
        il.Emit(
OpCodes
.Ret);     
        _del.Add(key, dm);
    }
 
   
public override void Copy(object source, object
target)
    {
       
var
sourceType = source.GetType();
       
var
targetType = target.GetType();
       
var
key = GetMapKey(sourceType, targetType);
 
       
var
del = _del[key];
       
var args = new
[] { source, target };
        del.Invoke(
null, args);
    }
}

Now let’s see how well this implementation performs. The result is a little bit unexpected:0,0084 ms. Okay, there is a little gotcha but let it be your homework if you have nothing better to do.

Test method

Here is my simple test method that measures how much time each implementation takes with given to objects. After measuring it writes results to console window. You can use this code to make testing easier.

public static void TestMappers(object source, object target)
{
   
var mappers = new ObjectCopyBase
[]
                      {
                         
new ObjectCopyReflection
(),
                         
new ObjectCopyDynamicCode
(),
                         
new ObjectCopyLcg
()
                      };
   
var
sourceType = source.GetType();
   
var
targetType = target.GetType();
 
   
foreach (var mapper in
mappers)
    {
        mapper.MapTypes(sourceType, targetType);
 
       
var stopper = new Stopwatch
();
        stopper.Start();
 
       
for (var
i = 0; i < 100000; i++)
        {
            mapper.Copy(source, target);
        }
 
        stopper.Stop();
 
       
var time = stopper.ElapsedMilliseconds / (double
)100000;
       
Console.WriteLine(mapper.GetType().Name + ": " + time);  
    }
}

Summary

In this posting I showed you how I organized my previously written dirty code to classes and achieved better readability and maintainability of code. Abstract base class serves also very well – I am able to cast different mapper instances to base type and therefore it is easier for me to measure their performance and to use them with IoC containers like Unity.

In the next posting about object to object mapper I will show you how to modify these classes to gain better performance.

Gunnar Peipman

Gunnar Peipman is ASP.NET, Azure and SharePoint fan, Estonian Microsoft user group leader, blogger, conference speaker, teacher, and tech maniac. Since 2008 he is Microsoft MVP specialized on ASP.NET.

    3 thoughts on “Writing object to object mapper: first implementations

    Leave a Reply

    Your email address will not be published. Required fields are marked *