本文介绍了我怎么能转换不同的(但兼容)模型之间的lambda表达式?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

(基于电子邮件的谈话,现在记录的信息共享),我已经在不同的层中使用两种模式:

(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; }
}

和在我的DTO层方面的lambda:

and a lambda in terms of my DTO layer:

Expression<Func<TestDTO, bool>> fc1 =
   (TestDTO c1) => c1.CustomerID <= 100 && c1.CustomerID >= 10;



我如何可以转换的lambda(一般情况下),以谈论其他的型号:

How can I convert that lambda (in the general case) to talking about the other model:

Expression<Func<Test, bool>> fc2 = {insert magic here, based on fc1}



(很明显,我们是同样的测试之后, -condition,但使用测试键入)

推荐答案

要做到这一点,你必须完全重建表达树;参数将需要重新映射,并且是现在所说不同类型的所有成员访问将需要被重新应用。幸运的是,很多本是由由 ExpressionVisitor 类更容易;例如(做这一切在一般情况下,不只是 Func键< T,BOOL> 谓词使用):

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; for example (doing it all in the general case, not just the Func<T,bool> predicate usage):

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);
    }
}



在这里,我们传递的参数重新字典 - 地图,应用,在 VisitParameter 。我们在 VisitMember ,检查,看看是否我们交换类型(如访问涉及它可以发生 ParameterExpression 或其他 MemberExpression ,在任何时候):如果我们有,我们会努力找到相同的另一名成员。名称

Here, we pass in a dictionary of parameters to re-map, applying that in 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.

接下来,我们需要一个通用的lambda转换重写方​​法:

Next, we need a general purpose lambda-conversion rewriter method:

// 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);
}

这需要一个任意表达式来; TFrom> TTO ,其转换为表达式来; TTO> ,方法是:

This takes an arbitrary Expression<TFrom>, and a TTo, converting it to an Expression<TTo>, by:


  • 发现那些类型是 TFrom / TTO

  • 使用的参数
  • 用我们刚创建的​​表达式的访问者

  • 并最终构建一个新的lambda表达式所需的签名

  • finding which types are different between TFrom / TTo
  • using that to re-map the parameters
  • using the expression-visitor we just created
  • and finally constructing a new lambda expression for the desired signature

然后,把他们放在一起,揭露我们的扩展方法:

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
}

等瞧;通用的lambda转换例程,以具体实施的:

et voila; a general-purpose lambda conversion routine, with a specific implementation of:

Expression<Func<Test, bool>> fc2 = fc1.Convert<TestDTO, Test>();

这篇关于我怎么能转换不同的(但兼容)模型之间的lambda表达式?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

11-03 10:11