[英]How can I convert a lambda-expression between different (but compatible) models?
(based on an email conversation, now recorded for information sharing) I have two models used at different layers: (基于电子邮件对话,现在记录用于信息共享)我在不同的层使用了两个模型:
public class TestDTO {
public int CustomerID { get; set; }
}
//...
public class Test {
public int CustomerID { get; set; }
}
and a lambda in terms of my DTO layer: 和我的DTO层的lambda:
Expression<Func<TestDTO, bool>> fc1 =
(TestDTO c1) => c1.CustomerID <= 100 && c1.CustomerID >= 10;
How can I convert that lambda (in the general case) to talking about the other model: 如何将lambda(在一般情况下)转换为讨论其他模型:
Expression<Func<Test, bool>> fc2 = {insert magic here, based on fc1}
(obviously, we're after the same test-condition, but using the Test
type) (显然,我们是在相同的测试条件之后,但使用Test
类型)
? ?
To do that, you'll have to rebuild the expression-tree completely; 要做到这一点,你必须完全重建表达式树; the parameters will need re-mapping, and all member-access that is now talking to different types will need to be reapplied. 参数将需要重新映射,并且需要重新应用现在与不同类型通信的所有成员访问。 Fortunately, a lot of this is made easier by the ExpressionVisitor
class; 幸运的是, ExpressionVisitor
类使这很容易。 for example (doing it all in the general case, not just the Func<T,bool>
predicate usage): 例如(在一般情况下完成所有操作,而不仅仅是Func<T,bool>
谓词用法):
class TypeConversionVisitor : ExpressionVisitor
{
private readonly Dictionary<Expression, Expression> parameterMap;
public TypeConversionVisitor(
Dictionary<Expression, Expression> parameterMap)
{
this.parameterMap = parameterMap;
}
protected override Expression VisitParameter(ParameterExpression node)
{
// re-map the parameter
Expression found;
if(!parameterMap.TryGetValue(node, out found))
found = base.VisitParameter(node);
return found;
}
protected override Expression VisitMember(MemberExpression node)
{
// re-perform any member-binding
var expr = Visit(node.Expression);
if (expr.Type != node.Type)
{
MemberInfo newMember = expr.Type.GetMember(node.Member.Name)
.Single();
return Expression.MakeMemberAccess(expr, newMember);
}
return base.VisitMember(node);
}
}
Here, we pass in a dictionary of parameters to re-map, applying that in VisitParameter
. 在这里,我们传入一个参数字典来重新映射,在VisitParameter
应用它。 We also, in VisitMember
, check to see if we've switched type (which can happen if Visit
involves a ParameterExpression
or another MemberExpression
, at any point): if we have, we'll try and find another member of the same name. 我们还在VisitMember
中检查我们是否已切换类型(如果Visit
涉及ParameterExpression
或其他MemberExpression
,可能会发生这种情况):如果有,我们将尝试找到另一个同名的成员。
Next, we need a general purpose lambda-conversion rewriter method: 接下来,我们需要一个通用的lambda转换重写器方法:
// allows extension to other signatures later...
private static Expression<TTo> ConvertImpl<TFrom, TTo>(Expression<TFrom> from)
where TFrom : class
where TTo : class
{
// figure out which types are different in the function-signature
var fromTypes = from.Type.GetGenericArguments();
var toTypes = typeof(TTo).GetGenericArguments();
if (fromTypes.Length != toTypes.Length)
throw new NotSupportedException(
"Incompatible lambda function-type signatures");
Dictionary<Type, Type> typeMap = new Dictionary<Type,Type>();
for (int i = 0; i < fromTypes.Length; i++)
{
if (fromTypes[i] != toTypes[i])
typeMap[fromTypes[i]] = toTypes[i];
}
// re-map all parameters that involve different types
Dictionary<Expression, Expression> parameterMap
= new Dictionary<Expression, Expression>();
ParameterExpression[] newParams =
new ParameterExpression[from.Parameters.Count];
for (int i = 0; i < newParams.Length; i++)
{
Type newType;
if(typeMap.TryGetValue(from.Parameters[i].Type, out newType))
{
parameterMap[from.Parameters[i]] = newParams[i] =
Expression.Parameter(newType, from.Parameters[i].Name);
}
else
{
newParams[i] = from.Parameters[i];
}
}
// rebuild the lambda
var body = new TypeConversionVisitor(parameterMap).Visit(from.Body);
return Expression.Lambda<TTo>(body, newParams);
}
This takes an arbitrary Expression<TFrom>
, and a TTo
, converting it to an Expression<TTo>
, by: 这需要一个任意的Expression<TFrom>
和一个TTo
,通过以下方式将其转换为Expression<TTo>
:
TFrom
/ TTo
找出TFrom
/ TTo
之间哪些类型不同 Then, putting it all together and exposing our extension method: 然后,将它们放在一起并公开我们的扩展方法:
public static class Helpers {
public static Expression<Func<TTo, bool>> Convert<TFrom, TTo>(
this Expression<Func<TFrom, bool>> from)
{
return ConvertImpl<Func<TFrom, bool>, Func<TTo, bool>>(from);
}
// insert from above: ConvertImpl
// insert from above: TypeConversionVisitor
}
et voila; et voila; a general-purpose lambda conversion routine, with a specific implementation of: 一个通用的lambda转换例程,具体实现:
Expression<Func<Test, bool>> fc2 = fc1.Convert<TestDTO, Test>();
You could use AutoMapper (no expression tree): 您可以使用AutoMapper (无表达式树):
Mapper.CreateMap<Test, TestDTO>();
...
Func<TestDTO, bool> fc1 =
(TestDTO c1) => c1.CustomerID <= 100 && c1.CustomerID >= 10;
Func<Test, bool> fc2 =
(Test t) => fc1(Mapper.Map<Test, TestDTO>(t));
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.