gpt4 book ai didi

c# - 使用 List.Contains 方法为 LINQ 构建表达式树

转载 作者:太空狗 更新时间:2023-10-30 00:02:47 25 4
gpt4 key购买 nike

问题

我正在重构一些 LINQ在我们的 Web 应用程序中查询多个报告,我试图将一些重复的查询谓词移动到它们自己的 IQueryable 中扩展方法,以便我们可以将它们重用于这些报告和将来的报告。正如您可能推断的那样,我已经重构了组的谓词,但是代码的谓词给我带来了问题。这是我目前拥有的一种报告方法的示例:

DAL 方法:

public List<Entities.QueryView> GetQueryView(Filter filter)
{
using (var context = CreateObjectContext())
{
return (from o in context.QueryViews
where (!filter.FromDate.HasValue || o.RepairDate >= EntityFunctions.TruncateTime(filter.FromDate))
&& (!filter.ToDate.HasValue || o.RepairDate <= EntityFunctions.TruncateTime(filter.ToDate))
select o)
.WithCode(filter)
.InGroup(filter)
.ToList();
}
}

IQueryable扩展名:

public static IQueryable<T> WithCode<T>(this IQueryable<T> query, Filter filter)
{
List<string> codes = DAL.GetCodesByCategory(filter.CodeCategories);

if (codes.Count > 0)
return query.Where(Predicates.FilterByCode<T>(codes));

return query;
}

谓词:

public static Expression<Func<T, List<string>, bool>> FilterByCode<T>(List<string> codes)
{
// Method info for List<string>.Contains(code).
var methodInfo = typeof(List<string>).GetMethod("Contains", new Type[] { typeof(string) });

// List of codes to call .Contains() against.
var instance = Expression.Variable(typeof(List<string>), "codes");

var param = Expression.Parameter(typeof(T), "j");
var left = Expression.Property(param, "Code");
var expr = Expression.Call(instance, methodInfo, Expression.Property(param, "Code"));

// j => codes.Contains(j.Code)
return Expression.Lambda<Func<T, List<string>, bool>>(expr, new ParameterExpression[] { param, instance });
}

我遇到的问题是 Queryable.Where不接受 Expression<Func<T, List<string>, bool> 的类型.我能想到的动态创建此谓词的唯一方法是使用两个参数,这是真正难倒我的部分。

我不明白的是以下方法有效。我可以传递我尝试动态创建的确切 lambda 表达式,它会正确地过滤我的数据。

public List<Entities.QueryView> GetQueryView(Filter filter)
{
// Get the codes here.
List<string> codes = DAL.GetCodesByCategory(filter.CodeCategories);

using (var context = CreateObjectContext())
{
return (from o in context.QueryViews
where (!filter.FromDate.HasValue || o.RepairDate >= EntityFunctions.TruncateTime(filter.FromDate))
&& (!filter.ToDate.HasValue || o.RepairDate <= EntityFunctions.TruncateTime(filter.ToDate))
select o)
.Where(p => codes.Contains(p.Code)) // This works fine.
//.WithCode(filter)
.InGroup(filter)
.ToList();

}

}

问题

  1. 我可以自己实现吗 Queryable.Where重载?如果是这样,它是否可行?
  2. 如果重载不可行,有没有办法动态构造谓词 p => codes.Contains(p.Code)不使用两个参数?
  3. 有没有更简单的方法来做到这一点?我觉得我错过了什么。

最佳答案

  1. 您可以创建自己的扩展方法,将其命名为Where , 接受 IQueryable<T> , 返回 IQueryable<T> , 否则让它模拟 LINQ 方法的形式。它不会 LINQ 方法,但它看起来像一个。我不鼓励您编写这样的方法,因为它可能会使其他人感到困惑;即使您想创建一个新的扩展方法,也请使用 LINQ 中未使用的名称以避免混淆。简而言之,做你现在正在做的,创建新的扩展而不实际命名它们 Where .如果你真的想说出一个Where尽管没有什么能阻止你。

  2. 当然,只需使用 lambda:

    public static Expression<Func<T, bool>> FilterByCode<T>(List<string> codes)
    where T : ICoded //some interface with a `Code` field
    {
    return p => codes.Contains(p.Code);
    }

    如果你真的不能让你的实体实现一个接口(interface)(提示:你几乎肯定可以),那么代码看起来与你拥有的代码相同,但使用你传入的列表作为常量而不是新的参数:

    public static Expression<Func<T, bool>> FilterByCode<T>(List<string> codes)
    {
    var methodInfo = typeof(List<string>).GetMethod("Contains",
    new Type[] { typeof(string) });

    var list = Expression.Constant(codes);

    var param = Expression.Parameter(typeof(T), "j");
    var value = Expression.Property(param, "Code");
    var body = Expression.Call(list, methodInfo, value);

    // j => codes.Contains(j.Code)
    return Expression.Lambda<Func<T, bool>>(body, param);
    }

    我强烈建议使用前一种方法;此方法失去了静态类型安全性,并且更复杂,因此更难维护。

    另一个注意事项,您在代码中的注释:// j => codes.Contains(j.Code)不准确。 lambda 实际上 看起来像:(j, codes) => codes.Contains(j.Code);这实际上明显不同。

  3. 参见#2 的前半部分。

关于c# - 使用 List<T>.Contains 方法为 LINQ 构建表达式树,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30173455/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com