Gar*_*tes 5 c# linq entity-framework expression-trees
我正在努力LINQ为我们的Web应用程序中的几个报告重构一些查询,并且我试图将一些重复的查询谓词移动到他们自己的IQueryableexension方法中,以便我们可以将它们重用于这些报告,并在将来报告.正如您可能推断的那样,我已经为组重构了谓词,但是代码的谓词给了我一些问题.这是我到目前为止报告方法之一的一个例子:
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();
}
}
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)
谓语:
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 });
}
Run Code Online (Sandbox Code Playgroud)
我遇到的问题是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();
}
}
Run Code Online (Sandbox Code Playgroud)
Queryable.Where重载吗?如果是这样,它甚至可行吗?p => codes.Contains(p.Code)而不使用两个参数?Ser*_*rvy 15
您可以创建自己的扩展方法,命名Where,接受IQueryable<T>,返回IQueryable<T>,以及使其模拟LINQ方法的形式.它不会是一个LINQ的方法,但它看起来像一个.我不鼓励你写这样的方法只是因为它可能会混淆他人; 即使您想要创建一个新的扩展方法,也可以使用LINQ中未使用的名称来避免混淆.简而言之,做你现在正在做的事情,创建新的扩展而不实际命名它们Where.如果你真的想要命名一个,Where但没有什么能阻止你.
当然,只需使用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);
}
Run Code Online (Sandbox Code Playgroud)
如果你真的不能让你的实体实现一个接口(提示:你几乎可以肯定),那么代码看起来与你拥有的代码相同,但是使用你作为常量而不是新参数传递的列表:
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);
}
Run Code Online (Sandbox Code Playgroud)
我强烈建议使用前一种方法; 这种方法失去了静态类型的安全性,并且更加复杂,因此难以维护.
另请注意,您的代码中的注释:// j => codes.Contains(j.Code)不准确.lambda 实际上是什么样的: (j, codes) => codes.Contains(j.Code);实际上明显不同.
见#2的前半部分.