【问题标题】:Append Ef Core IQueryable< T> as Or Query追加 Ef Core IQueryable<T> 作为或查询
【发布时间】:2021-09-01 10:29:16
【问题描述】:

我有主要的 EF 核心查询,它通过 IQueryable.Where() 向它附加一些 AND 搜索条件。没问题。

并通过Expression&lt;Func&lt;T, bool&gt;&gt; orFilter 对其应用一些 OR 搜索条件。 它与 Or via 连接:orFilter = orFilter.Or(p=&gt;p.Prop == foo); 最后,它将通过 mainQuery.Where(orFilter) 作为 AND 搜索条件应用于主查询;没问题。

但是 OR 搜索条件之一是针对另一个表,需要连接到另一个表,它返回 IQueryable。我想通过orFilter = orFilter.Or(joinQuery.Expression as Expression&lt;Func&lt;Product, bool&gt;&gt;); 将它应用到 orFilter 但它不起作用。

或者有什么等效的实现方式吗?

IQueryable<Product> records = DBContext.Product
        .Include(p => p.Item1)
        ...;


if (filterANDCriteria1.HasValue)
{
   records= records.Where(p.xxx == filterANDCriteria1);
}

Expression<Func<Product, bool>> orFilter= null;
if(filterOrCriteriaP1.HasValue)
{
 orFilter= orFilter.Or(p=>p.yyy==filterOrCriteriaP1 );
}

if(filterOrCriteriaP2.HasValue)
{
//join another table e.g. Category pseudo code 
  IQueryable<Product> joinQuery = 
records.Join(Category.EntityId == Product.Id and Category.EntityType == EntityType.Product and Category.FieldFoo == filterOrCriteriaP2), 
(product,category)=> product;

  //Want to append the query to orFilter here but it not work and return null
  orFilter = orFilter.Or(joinQuery.Expression as Expression<Func<Product, bool>>); //return null

//It only works for AND query, following code will return the products that match join result and other filter (like filterANDCriteria1)
// records = joinQuery;
}
if(orFilter!=null)
{
   //Apply whole orFilter as AND filter to main query
   records = records.Where(orFilter);
}
return records.ToList();

【问题讨论】:

标签: c# entity-framework entity-framework-core


【解决方案1】:

这里的代码可以帮助你。

public static class PredicateBuilder
{
    public static IQueryable<T> WhereAny<T>(
        this IQueryable<T> source,
        params Expression<Func<T, bool>>[] predicates)
    {
        if (source == null) throw new ArgumentNullException("source");
        var exp = GetPredicates(predicates);
        return source.Where(exp);
    }
    private static Expression<Func<T, bool>> GetPredicates<T>(Expression<Func<T, bool>>[] predicates)
    {
        if (predicates == null) throw new ArgumentNullException("predicates");
        if (predicates.Length == 0) return x => false;
        if (predicates.Length == 1) return predicates[0];

        var exp = predicates[0];
        for (var i = 1; i < predicates.Length; i++)
        {
            exp = exp.OrElse(predicates[i]);
        }
        return exp;
    }
    public static Expression<Func<T, bool>> OrElse<TA, T>(this IEnumerable<TA> args, Func<TA, Expression<Func<T, bool>>> expression)
    {
        return args.Select(expression).ToList().OrElse();
    }
    public static Expression<Func<T, bool>> OrElse<T>(this List<Expression<Func<T, bool>>> expressions)
    {
        if (expressions.Count <= 0) return null;
        var expression = expressions[0];
        for (var i = 1; i < expressions.Count; i++)
        {
            expression = expression.OrElse(expressions[i]);
        }
        return expression;
    }
    public static Expression<Func<T, bool>> OrElse<T>(
        this Expression<Func<T, bool>> expr1,
        Expression<Func<T, bool>> expr2)
    {
        var parameter = Expression.Parameter(typeof(T), expr1.Parameters[0].Name);
        var leftVisitor = new ReplaceExpressionVisitor(expr1.Parameters[0], parameter);
        var left = leftVisitor.Visit(expr1.Body);
        var rightVisitor = new ReplaceExpressionVisitor(expr2.Parameters[0], parameter);
        var right = rightVisitor.Visit(expr2.Body);
        return Expression.Lambda<Func<T, bool>>(
            Expression.OrElse(left, right), parameter);
    }
    public static Expression<Func<T, bool>> AndAlso<T>(
       this Expression<Func<T, bool>> expr1,
       Expression<Func<T, bool>> expr2)
    {
        var parameter = Expression.Parameter(typeof(T), expr1.Parameters[0].Name);
        var leftVisitor = new ReplaceExpressionVisitor(expr1.Parameters[0], parameter);
        var left = leftVisitor.Visit(expr1.Body);
        var rightVisitor = new ReplaceExpressionVisitor(expr2.Parameters[0], parameter);
        var right = rightVisitor.Visit(expr2.Body);
        return Expression.Lambda<Func<T, bool>>(
            Expression.AndAlso(left, right), parameter);
    }
    private class ReplaceExpressionVisitor : ExpressionVisitor
    {
        private readonly Expression _oldValue;
        private readonly Expression _newValue;

        public ReplaceExpressionVisitor(Expression oldValue, Expression newValue)
        {
            _oldValue = oldValue;
            _newValue = newValue;
        }

        public override Expression Visit(Expression node)
        {
            if (node == _oldValue)
                return _newValue;
            return base.Visit(node);
        }
    }
}

使用演示:

var users = ctx.Users.WhereAny(ids.Select(id => (Expression<Func<T, bool>>)(g => g.Id == id)).ToArray()).ToList();

你可以有新的扩展方法,那么它使用起来更容易。

    public static IQueryable<User> WhereAnyId(
        this IQueryable<User> source,
        params Guid[] ids)
    {
        if (source == null) throw new ArgumentNullException(nameof(source));
        return source.WhereAny(ids.Select(id => (Expression<Func<User, bool>>)(g => g.Id == id)).ToArray());
    }

    var users = ctx.Users.WhereAnyId(ids).ToList();

【讨论】:

    【解决方案2】:

    我终于找到了解决办法。 由于它是一对一映射,因此我在 Product 表中添加了一个新列,例如命名为 CategoryId。创建产品类别时,将 CategoryId 填回 Product 表中。

    并包含类型为Category的属性并添加数据注释[ForeignKey]以指向CategoryId。 所以可以使用 .Include(p=>p.Category) 并使用 orFilter = orFilter.Or(p=>p.Category.xxx == filterOrCriteriaP2.Value);

    对于其他实体,例如订单,不受影响。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-01-18
      • 2019-04-28
      • 2012-01-10
      • 2018-10-17
      • 2022-01-09
      • 2020-03-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多