我需要在运行时构建一个复杂的lambda表达式,这里是cenario:
我有一个名为IHasCostCenter的接口,其字符串属性名为CostCenterId:
public interface IHasCostCenter
{
string CostCenterId { get; set; }
}
以下是实现此接口的类的示例:
public class Contract_Rate: Entity, IHasCostCenter
{
public int ContratoId { get; set; }
public string CostCenterId { get; set; }
public int Percentage { get; set; }
}
有两种情况我需要构建动态lambda表达式,第一种情况是,如果类包含IHasCostCenter接口,它应该根据CostCenterId和项目的CostCenterId列表过滤项目,这是表达式建:
if (Interfaces.Any(x => x == typeof(IHasCostCenter)))
{
var sValues = User.CostCenters.Select(x => x.CostCenterId).ToList();
var sValuesType = sValues.GetType().GetGenericArguments().FirstOrDefault();
ParameterExpression Parameter = Expression.Parameter(typeof(T), "x");
Expression Property = Expression.Property(Parameter, typeof(T).GetProperty("CostCenterId"));
Expression Contains = Expression.Call(typeof(Enumerable), "Contains", new[] { sValuesType }, Expression.Constant(sValues, sValues.GetType()), Property);
MethodCallExpression whereExpression = Expression.Call(
typeof(Queryable),
"Where",
new Type[] { items.ElementType },
items.Expression,
Expression.Lambda(Contains, Parameter));
items = items.Provider.CreateQuery<T>(whereExpression);
}
还有第二个中心,而这个我无法解决,它发生在对象包含实现IHasCostCenter接口的对象列表时,就像上面描述的类一样,在这种情况下lambda表达式应该选择Contract_Rate合同中的列表,然后为列表中的每个Contract_Rate选择CostCenterId,并查看是否有任何CostCenterId位于User.CostCenters.Select(x =&gt; x.CostCenterId).ToList()。
任何帮助都将不胜感激。
答案 0 :(得分:-1)
构建使用lambdas编写此查询所需的工具比尝试手动创建整个表达式更容易。
最简单的情况是查询中的项目实现IHasCostCenter
。在这种情况下,您可以简单地将泛型参数约束为IHasCostCenter
,然后使用lambda:
public static IQueryable<T> Foo<T>(
this IQueryable<T> query)
where T : IHasCostCenter
{
var validIDs = User.CostCenters.Select(cc => cc.CostCenterId);
return query.Where(item => validIDs.Contains(item.CostCenterId));
}
如果您选择一个选择器来选择有问题的项目到IHasCostCenter
,并假设查询具有该类型的属性,然后编写您自己的lambda来转换{{1}在确定它是否是有效ID的查询中,您可以IHasCostCenter
将这两个lambdas放在一起。如果您编写这样的Compose
方法,那么现有代码的实现将变为:
Compose
将此转换为接受public static IQueryable<T> Foo<T>(
this IQueryable<T> query,
Expression<Func<T, IHasCostCenter>> selector)
{
var validIDs = User.CostCenters.Select(cc => cc.CostCenterId);
return query.Where(selector.Compose(
item => validIDs.Contains(item.CostCenterId)));
}
变得非常简单:
IEnumerable<IHasCostCenter>>
那么我们如何编写public static IQueryable<T> Foo<T>(
this IQueryable<T> query,
Expression<Func<T, IEnumerable<IHasCostCenter>>> selector)
{
var validIDs = User.CostCenters.Select(cc => cc.CostCenterId);
return query.Where(selector.Compose(list =>
list.Any(item => validIDs.Contains(item.CostCenterId))));
}
方法呢?我们需要做的是接受两个表达式,并用组合表达式的主体替换组合表达式的所有参数实例:
Compose
这使用以下方法将一个表达式的所有实例替换为另一个:
public static Expression<Func<TFirstParam, TResult>>
Compose<TFirstParam, TIntermediate, TResult>(
this Expression<Func<TFirstParam, TIntermediate>> first,
Expression<Func<TIntermediate, TResult>> second)
{
var param = Expression.Parameter(typeof(TFirstParam), "param");
var newFirst = first.Body.Replace(first.Parameters[0], param);
var newSecond = second.Body.Replace(second.Parameters[0], newFirst);
return Expression.Lambda<Func<TFirstParam, TResult>>(newSecond, param);
}