I’ve bending my mind for a while, but I think I’m missing something, so may be someone will help.
Let’s say I have following mapper class:
public class Mapping<TSource, TResult>
{
private readonly Action<TSource, TResult> setter;
public Mapping(Expression<Func<TSource, TResult>> expression)
{
var newValue = Expression.Parameter(expression.Body.Type);
var body = Expression.Assign(expression.Body, newValue);
var assign = Expression.Lambda<Action<TSource, TResult>>(body, expression.Parameters[0], newValue);
setter = assign.Compile();
}
public void Assign(TSource instance, TResult value)
{
setter(instance, value);
}
}
And it is working fine:
[Test]
public void ShouldMapProperty()
{
var testClass = new TestClass();
var nameMapping = new Mapping<TestClass, string>(x => x.Name);
var ageMapping = new Mapping<TestClass, int>(x => x.Age);
nameMapping.Assign(testClass, "name");
ageMapping.Assign(testClass, 10);
Assert.AreEqual("name", testClass.Name);
Assert.AreEqual(10, testClass.Age);
}
Thing is, that I would like to keep mappings for single object type into some collection and TResult is getting in the way, as long as different properties have different types.
How to get rid of TResult nicely?
Update:
looks like I wasn’t clear enough, so this would be sample how would I use it:
public class Mapping<TSource, TResult>
{
private readonly Action<TSource, TResult> setter;
private readonly string columnName;
public Mapping(Expression<Func<TSource, TResult>> expression, string columnName)
{
this.columnName = columnName;
var newValue = Expression.Parameter(expression.Body.Type);
var body = Expression.Assign(expression.Body, newValue);
var assign = Expression.Lambda<Action<TSource, TResult>>(body, expression.Parameters[0], newValue);
setter = assign.Compile();
}
public void Assign(TSource instance, DataRow row)
{
setter(instance, row[columnName]);
}
}
And then I would have some MappingConfiguration class, that would let me do this:
MappingConfiguration.For<TestClass>()
.Map(x => x.Name, "FirstName")
.Map(x => x.Age, "Age");
And finaly some MappingEngine class, that would take DataTable and MappingConfiguration as input and produce IEnumerable<TestClass> as output.
Update 2:
I’ve modified initial version to this:
public class Mapping2<TSource>
{
private readonly Delegate setter;
public Mapping2(Expression<Func<TSource, object>> expression)
{
var newValue = Expression.Parameter(expression.Body.Type);
var body = Expression.Assign(expression.Body, newValue);
var assign = Expression.Lambda(body, expression.Parameters[0], newValue);
setter = assign.Compile();
}
public void Assign(TSource instance, object value)
{
setter.DynamicInvoke(instance, value);
}
}
And it almost works.
By almost I mean it works with reference type properties, and with value type properties I get:
System.ArgumentException : Expression must be writeable
Parameter name: left
I’ve managed to do it, source code below. It runs somewhat faster than Automapper (not sure if my Automapper configuration is the fastest for this task), benchmark is not bulletproof, but on my machine to map 5 million rows took 20.16 seconds using my written mapper and 39.90 using Automapper, although it seems that Automapper uses less memory for this task (haven’t measured it, but with 10 million rows Automapper gives result and my mapper fails with OutOfMemory).