如何创建基于两个模型的动态 lambda 表达式?

How to create a dynamic lambda expression based on two models?

我有两个类。

public class First
{
public int P {get; set;}
public int A {get; set;}
public int B {get; set;}
}
public class Second
{
public int P {get; set;}
public int C {get; set;}
}

我想计算这样的东西。

var first = A collection of First,
var second = A collection of Second

first.Select(f=> f.A * second.FirstOrDefault(s => s.P == f.P).C).Sum();

我可以使用 ()

在表达式中更改 f=> f.A
ParameterExpression f =  Expression.Parameter(typeof(First), 'f');
Expression a = Expression.Property(f, "A");
var lambda = Expression.Lambda(Func<First, decimal>(a, f));
lambda.Complie()

我尝试使用内部 lambda

在表达式中更改 s => s.P == f.P
ParameterExpression f =  Expression.Parameter(typeof(First), 'f');
ParameterExpression s =  Expression.Parameter(typeof(Second), 's');
Expression fp = Expression.Property(f, "P");
Expression sp = Expression.Property(s, "P");
Expression finalExp = Expression.Equal(sp, fp);
var lambda = Expression.Lambda(Func<Second, bool>(finalExp , s));
lambda.Complie()

我在这段代码中遇到了两个问题。

  1. 内部 lambda 代码在 lambda.Complie() 处失败。错误:- 从范围“”引用了类型 'First' 的系统 linq 表达式变量 'f',但未定义。
  2. 在first.Select(f=> f.A * second.FirstOrDefault(s => s.P == f.P).C), f=> f.A是一个表达式,另一部分是小数,不能相乘。

假设您需要通用方法来处理此类表达式:

public static int CalcSum<TFirst, TSecond>(IEnumerable<TFirst> first, IEnumerable<TSecond> second)
{
    var f = Expression.Parameter(typeof(TFirst), "f");
    var s = Expression.Parameter(typeof(TSecond), "s"); 

    var firstQueryable = first.AsQueryable();
    var secondQueryable = second.AsQueryable();

    // s => f.P == s.P
    var firstOrDefaultFilter = Expression.Lambda(
        Expression.Equal(Expression.Property(f, "P"), Expression.Property(s, "P")),
        s);

    // second.FirstOrDefault(f.P == s.P)
    var firstOrDefault = Expression.Call(typeof(Queryable), nameof(Queryable.FirstOrDefault),
        new[] { typeof(TSecond) }, secondQueryable.Expression, firstOrDefaultFilter);

    // second.FirstOrDefault(f.P == s.P).C
    var propertyToSum = Expression.Property(firstOrDefault, "C");

    // f => f.A * second.FirstOrDefault(f.P == s.P).C
    var selectLambda = Expression.Lambda(
        Expression.Multiply(Expression.Property(f, "A"), propertyToSum),
        f);

    // first.Select(f => f.A * second.FirstOrDefault(f.P == s.P).C)
    var queryExpr = Expression.Call(typeof(Queryable), nameof(Queryable.Select),
        new Type[] { typeof(TFirst), typeof(int) }, firstQueryable.Expression, selectLambda);

    var query = firstQueryable.Provider.CreateQuery<int>(queryExpr);

    return query.Sum();
}