在表达式树中使用局部变量
本文关键字:局部变量 表达式 | 更新日期: 2023-09-27 18:00:16
我有一个LINQ表达式,它可以查找给定客户的CreditBalance:的所有历史更改
var history = GetHistory(id);
var changes = history.Where(x => history.Where(y => y.AuditId < x.AuditId)
.OrderByDescending(y => y.AuditId)
.Select(y => y.CreditBalance)
.FirstOrDefault() != x.CreditBalance);
此功能按预期工作。我想做的是更改此功能,允许用户查询任何历史字段的更改。我选择用表达树来解决这个问题。
到目前为止,我已经想出了这个解决方案:
var history = GetHistory(id);
var c = Expression.Parameter(typeof(Customer_history), "c");
var d = Expression.Parameter(typeof(Customer_history), "d");
var cAudit = Expression.Property(c, typeof(Customer_history).GetProperty("AuditId"));
var dAudit = Expression.Property(d, typeof(Customer_history).GetProperty("AuditId"));
var whereBody = Expression.LessThan(dAudit, cAudit);
var whereLambda = Expression.Lambda(whereBody, d);
var where = Methods.QueryableWhere.MakeGenericMethod(typeof(Customer_history));
var whereCall = Expression.Call(null, where, **Expression.Constant(history)**, whereLambda);
var orderByLambda = Expression.Lambda(dAudit, d);
var orderBy = Methods.QueryableOrderByDescending.MakeGenericMethod(typeof(Customer_history), orderByLambda.Body.Type);
var orderByCall = Expression.Call(null, orderBy, whereCall, orderByLambda);
var dProp = Expression.Property(d, typeof(Customer_history).GetProperty(field));
var selectLambda = Expression.Lambda(dProp, d);
var select = Methods.QueryableSelect.MakeGenericMethod(typeof(Customer_history), selectLambda.Body.Type);
var selectCall = Expression.Call(null, select, orderByCall, selectLambda);
var firstOrDefault = Methods.QueryableFirstOrDefault.MakeGenericMethod(selectLambda.Body.Type);
var firstOrDefaultCall = Expression.Call(null, firstOrDefault, selectCall);
var cProp = Expression.Property(c, typeof(Customer_history).GetProperty(field));
var comparison = Expression.NotEqual(firstOrDefaultCall, cProp);
var lambda = Expression.Lambda<Func<Customer_history, bool>>(comparison, c);
var changes = history.Where(lambda);
问题是,当执行查询时,我得到了这个异常:
无法创建类型的常量值"Namespace.Customer_history"。仅基元类型或枚举在此上下文中支持类型。
现在我假设问题是基于异常消息的Expression.Constant(history)语句。问题是,我不知道如何重写它以允许查询提供程序适当地处理它。我知道它之所以有效是因为原始查询,只是不知道如何在表达式树中执行它。
有人能提供指导吗?
正如所怀疑的那样,ConstantExpression似乎不是从局部变量中获取值的方法。
我需要做的是创建一个私有类来存储变量,然后我可以使用字段MemberExpression 来访问它
private class ValueHolder<T>
{
public IQueryable<T> History;
}
然后在我的方法中,我能够使用以下方法评估表达式:
var valueHolder = new ValueHolder<T>
{
History = data
};
var c = Expression.Parameter(typeof(T), "c");
var constantEx = Expression.Constant(valueHolder);
var fieldEx = Expression.Field(constantEx, valueHolder.GetType().GetField("History"));
您可以尝试使用动态linq,它允许您将字符串用作表达式,而不是lambda。
示例:
var query = history.Where("MyField = MyFilter");
https://www.nuget.org/packages/System.Linq.Dynamic.Library/
https://github.com/NArnott/System.Linq.Dynamic