How to convert an expression tree?
UPDATE:
I'll try to explain what I mean. There are 2 different classes (MyClass1 and MyClass2) and method to convert class1 to class2:
class MyClass1
{
//...Some fields and properties
}
class MyClass2
{
//...Some fields and properties
}
public MyClass2 Convert(MyClass1 class1)
{
//.....
return class2Object;
}
There are 2 different methods:
void method1(Expression<Func<MyClass1, bool>> where, //other parameters)
{
//some operations
//...............
//need to call method2(Expression<Func<MyClass2, bool>>)
// BUT! How do I convert Expression<Func<MyClass1, bool>>
// to Expression<Func<MyClass2, bool>>
}
void method2(Expression<Func<MyClass2, bool>> where, //other parameters)
{
//some operations
}
How do I convert Expression开发者_开发问答< Func< MyClass1, bool>> to Expression< Func< MyClass2, bool > >
Let me guess what you are asking: Your MyClass1
and MyClass2
look the same (they both have an int field1 and a string field2). Now you have an Expression<Func<MyClass1,bool>>
, something like:
Expression<Func<MyClass1, bool>> exp1 = x => x.field1 == 100; // x is MyClass1
And you want another expression, which looks the same, but it's for MyClass2
:
Expression<Func<MyClass2, bool>> exp2 = x => x.field1 == 100; // x is MyClass2
If this is what you are asking, here is my answer:
To get the expression for MyClass2
, you need to replace all x
in exp1
, because all x
in exp1 are of type MyClass1
. ExpressionVisitor is exactly what you want.
class MyExpressionVisitor : ExpressionVisitor
{
public ParameterExpression NewParameterExp { get; private set; }
public MyExpressionVisitor(ParameterExpression newParameterExp)
{
NewParameterExp = newParameterExp;
}
protected override Expression VisitParameter(ParameterExpression node)
{
return NewParameterExp;
}
protected override Expression VisitMember(MemberExpression node)
{
if (node.Member.DeclaringType == typeof(MyClass1))
return Expression.MakeMemberAccess(this.Visit(node.Expression),
typeof(MyClass2).GetMember(node.Member.Name).FirstOrDefault());
return base.VisitMember(node);
}
}
The visitor will go through(say "visit") the whole expression, visit all the nodes. When it comes to an ParameterExpression
node, we change the node (because it's MyClass1, we change it to MyClass2, see VisitParameter method). Another thing we need to change is, when the visitor comes to a node like x.field1
, it's visiting the field1 in MyClass1
, we need to modify it too(see VisitMember). After going through the whole exp1, we get a totally new exp2, with some nodes replaced, that's what we want.
Expression<Func<MyClass1, bool>> exp1 = x => x.field1 == 100;
var visitor = new MyExpressionVisitor(Expression.Parameter(typeof(MyClass2),
exp1.Parameters[0].Name));
var exp2 = Expression.Lambda<Func<MyClass2, bool>>
(visitor.Visit(exp1.Body), visitor.NewParameterExp);
//the following is for testing
var data = new MyClass2();
Console.WriteLine(exp2.Compile()(data)); //False
data.field1 = 100;
Console.WriteLine(exp2.Compile()(data)); //True
Expression trees are immutable, so to do this ou would need to walk the entire tree, rebuilding it and substiting any uses of the type with the equivalent - usually by writing a "visitor". When encountering a MemberExpression or MethodCallExpression, you would check the member's declaring type - if it is the one ou don't want, recreate it (Expression.PropertyOrField is useful here).
Note that you can't do this just at the places it is used; the entire tree must be regenerated. I'm not at a PC at the moment, but if you want I can do an example later; leave a comment if you need this example.
Note that this is somewhat complicated by the int/long and char/string mismatch.
public CategoryViewModel GetSingle( Expression<Func<CategoryViewModel, bool>> where)
{
Expression<Func<DAL.EntityModels.Category, CategoryViewModel>> converter =
c => ToBll(c);
var param = Expression.Parameter(typeof(DAL.EntityModels.Category), "category");
var body = Expression.Invoke(where, Expression.Invoke(converter, param));
var lambda = Expression.Lambda<Func<DAL.EntityModels.Category, bool>>(body, param);
return (CategoryViewModel )_categoryRepository.GetSingle(lambda);
}
//..............
public T GetSingle(Expression<Func<T, bool>> where)
{
return this.ObjectSet.Where(where).FirstOrDefault<T>();
}
You could compile the first expression to a delegate and then convert it with
NJection.LambdaConverter which is a library that converts delegates to expression trees.
精彩评论