我正在构建一种方法,该方法采用一个或多个条件来使用 LINQ 查询数据库。我做的:
public ICollection<MyClass> FindAllBy(params Expression<Func<MyClass, bool>>[] criteria)
{
using (var ctx = new MyContext())
{
IQueryable<MyClass> result = ctx.MyClasses.AsNoTracking();
if (criteria != null && criteria.Length > 0)
{
foreach (var item in criteria)
{
result = result.Where(item);
}
}
return result.ToList();
}
}
Run Code Online (Sandbox Code Playgroud)
这具有的效果是,如果我查找一个 Id 为 1 的对象和一个 Id 为 2 的对象,我将一无所获,因为没有行同时具有 1 和 2 的 Id。所以我需要一个 OR 子句。我找到了这个页面:
http://www.albahari.com/nutshell/predicatebuilder.aspx
其中有一个 PredicateBuilder 类,我用它来做这个:
public ICollection<PhotoFile> FindAllBy(params Expression<Func<PhotoFile, bool>>[] criteria)
{
using (var ctx = new CotanContext())
{
var predicate = PredicateBuilder.False<PhotoFile>();
if (criteria != null && criteria.Length > 0)
{
foreach (var item in criteria)
{
predicate = predicate.Or(item);
}
}
return ctx.PhotoFiles.Where(predicate).ToList();
}
}
Run Code Online (Sandbox Code Playgroud)
我的代码与页面略有不同,因为我将表达式传递到方法中,然后将其传递到 Predicate.Or 方法中。
上面的方法The LINQ expression node type 'Invoke' is not supported in LINQ to Entities.
报错。这是有道理的,因为实体框架不知道如何将此代码转换为有效的查询。
链接站点上的解决方案是下载他们的 Nuget 包或源代码,这使得该代码可以工作。但是,我真的不太愿意为单个函数放入数百行未知且看似未经测试的代码,在我看来,这些代码早在 Microsoft 很久以前就应该内置到 LINQ 中。我项目的首席开发人员过去也强烈建议不要使用不是直接来自 Microsoft 的未知包。我正在处理敏感信息,所以我宁愿安全也不要抱歉。
所以,我的问题是:有没有办法在 LINQ 中获得 OR 函数而不必使用外部 Nuget 包?
正如我在评论中提到的,您可以使用Universal PredicateBulder或我的答案中的类在 linq toEntity where 子句中建立两个列表之间的链接。
但是,您可以使用以下简单的扩展方法极大地简化示例中的方法:
public static class QueryableExtensions
{
public static IQueryable<T> WhereAny<T>(this IQueryable<T> source, IEnumerable<Expression<Func<T, bool>>> predicates)
{
if (predicates == null || !predicates.Any()) return source;
var predicate = predicates.Aggregate((a, b) => Expression.Lambda<Func<T, bool>>(
Expression.OrElse(a.Body, b.Body.ReplaceParameter(b.Parameters[0], a.Parameters[0])),
a.Parameters[0]));
return source.Where(predicate);
}
}
Run Code Online (Sandbox Code Playgroud)
它又使用这个助手:
public static class ExpressionUtils
{
public static Expression ReplaceParameter(this Expression expression, ParameterExpression source, Expression target)
{
return new ParameterReplacer { Source = source, Target = target }.Visit(expression);
}
class ParameterReplacer : ExpressionVisitor
{
public ParameterExpression Source;
public Expression Target;
protected override Expression VisitParameter(ParameterExpression node)
{
return node == Source ? Target : base.VisitParameter(node);
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在示例方法可以很简单:
public ICollection<PhotoFile> FindAllBy(params Expression<Func<PhotoFile, bool>>[] criteria)
{
using (var ctx = new CotanContext())
return ctx.PhotoFiles.WhereAny(criteria).ToList();
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
4439 次 |
最近记录: |