Reputation: 304
I have a class with a property that is another class. I also have an Expression that maps each from other classes. How do I combine these two expressions into one expression without compiling the second Expression?
public class ClassA
{
public int SomeProperty { get; set; }
}
public class MappedClassA
{
public int MappedProperty { get; set; }
}
public class ClassB
{
public ClassA ClassAProperty { get; set; }
//snip...
}
public class MappedClassB
{
public MappedClassA MappedClassAProperty {get; set; }
}
public Expression<Func<ClassA, MappedClassA>> MapAExpression()
{
return a => new MappedClassA()
{
MappedProperty = a.SomeProperty
};
}
public Expression<Func<ClassB, MappedClassB>> MapBExpression()
{
return b => new MappedClassB()
{
//this should be done with the above expression
MappedClassAProperty = new MappedClassA()
{
MappedProperty = b.ClassAProperty.SomeProperty
}
};
}
Upvotes: 1
Views: 146
Reputation: 13799
This is possible without compiling the expression, but unfortunately not by using the simple lambda syntax to construct the expression.
You will have to create the expression tree "by hand", using expression methods like Expression.New() and Expression.Lambda().
This can get unreadable very quickly.
I tried assembling such an expression from memory below, but I lack practice; this is untested and there are possibly some bugs left.
public Expression<Func<ClassB, MappedClassB>> MapBExpression()
{
// generate a parameter of type ClassB.
// This is the parameter "b" our final lambda expression will accept.
var classBparam = Expression.Parameter(typeof(ClassB));
// access b.ClassAProperty; this is the property
// that we want to pass to the expression returned by MapAExpression()
var memberAccess = Expression.MakeMemberAccess(
classBparam,
typeof(ClassB).GetProperty("ClassAProperty"));
// invoke the lambda returned by MapAExpression()
//with the parameter b.ClassAProperty
var invocation = Expression.Invoke( MapAExpression(), memberAccess );
// create a new MappedClassB(), this is the object that will be returned
// by the expression we are currently creating
var ctor = Expression.New(typeof(MappedClassB));
// We want to assign something to the MappedClassB.MappedClassAProperty
var mappedClassAProperty =
typeof(MappedClassB).GetProperty("MappedClassAProperty");
// specifically, we want to assign the result of our MapAExpression(),
// when invoked with the parameter b.ClassAProperty
var mappedClassAAssignment =
Expression.Bind(mappedClassAProperty, invocation);
// Here we initialize the MappedClassAProperty,
// after creating the new MappedClassB.
// We initialize it with the assignment we just created
var memberInit = Expression.MemberInit(ctor, mappedClassAAssignment);
// finally, we construct the lambda
//that does all of the above, given a parameter of type ClassB
return Expression.Lambda<Func<ClassB, MappedClassB>>(memberInit, classBparam);
// this expression should now be equivalent to:
// return b => new MappedClassB()
// {
// MappedClassAProperty = new MappedClassA()
// {
// MappedProperty = b.ClassAProperty.SomeProperty
// }
// };
}
Upvotes: 2