如何创建基于两个模型的动态 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()
我在这段代码中遇到了两个问题。
- 内部 lambda 代码在 lambda.Complie() 处失败。错误:- 从范围“”引用了类型 'First' 的系统 linq 表达式变量 'f',但未定义。
- 在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();
}
我有两个类。
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.AParameterExpression 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.PParameterExpression 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()
我在这段代码中遇到了两个问题。
- 内部 lambda 代码在 lambda.Complie() 处失败。错误:- 从范围“”引用了类型 'First' 的系统 linq 表达式变量 'f',但未定义。
- 在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();
}