【问题标题】:Subquery with "ANY" and local array generate nested too deep SQL Statement带有“ANY”和本地数组的子查询生成嵌套太深的 SQL 语句
【发布时间】:2015-03-16 14:30:38
【问题描述】:
public IEnumerable<Table1> GetMatchingTable1(string param, double[] Thicknesses)
{
    return DBContext.Table1.Where(c => c.Field1 == param
                                    && Thicknesses.Any(Thickness => Thickness >= c.MinThickness && Thickness <= c.MaxThickness))
                           .ToList();
}

以上查询返回以下异常。 “你的 SQL 语句的某些部分嵌套得太深。重写查询或将其分解为更小的查询。”

到目前为止,我在网络上针对此错误所做的所有研究都指向将“ANY”替换为“CONTAINS”。这是他们使用此解决方案解决问题的一个站点:http://blog.hompus.nl/2010/08/26/joining-an-iqueryable-with-an-ienumerable/
但就我而言,“CONTAINS”似乎不可用,因为我用 Min 和 Max 检查了 RANGE。

应该如何编写此查询以使 LinqToEntity 生成正确的 SQL 语句?

谢谢

【问题讨论】:

  • 您的收藏似乎太大了;您需要找到某种方法将其分解为多个较小的集合以进行检查。
  • 当本地数组中的元素很少时。它的工作原理是未达到 SQL 中的嵌套查询限制。但是超过 40 个时,达到了极限并引发了异常。由于本地数组“Thicknesses”来自用户输入,我不能保证它会很小。

标签: c# linq-to-entities subquery any


【解决方案1】:

您可以尝试动态构建查询:

public IEnumerable<Table1> GetAllCoilLengthSettingsWithChilds(string param, double[] Thicknesses)
{
    // Base query
    var query = LinqKit.Extensions.AsExpandable(DBContext.Table1.Where(c => c.Field1 == param));

    // All the various || between the Thickness ranges
    var predicate = LinqKit.PredicateBuilder.False<Table1>();

    foreach (double th in Thicknesses)
    {
        // Don't want a closure around th
        double th2 = th;
        predicate = predicate.Or(c => th2 >= c.MinThickness && th2 <= c.MaxThickness);
    }

    // This is implicitly in && with the other Where
    query = query.Where(predicate);

    return query.ToList();
}

PredicateBuilder 可帮助您构建 || 查询。从LinqKit 获取(来源可用) 我已经用 1000 个参数对其进行了测试(但它们在 DateTime 的位置,而且我没有其他查询片段),并且它似乎有效。请注意,该程序使用 LinqPad 的另一个扩展,AsExpandable,用于使PredicateBuilder“技巧”工作。请注意,我使用的是 EF 6.1.3,因此您的里程可能会有所不同。

如果您不想使用 LinqKit,我将附加 my 版本的 PredicateBuilder。它不需要使用AsExpandable(),但它的语法略有不同:

public class PredicateBuilder<T>
{
    // We share a single parameter for all the PredicatBuilder<T>
    // istances. This isn't a proble, because Expressions are immutable
    protected static readonly ParameterExpression Parameter = Expression.Parameter(typeof(T), "x");

    protected Expression Current { get; set; }

    // Returns an empty PredicateBuilder that, if used, is true
    public PredicateBuilder()
    {
    }

    // Use it like this: .Where(predicate) or .Any(predicate) or 
    // .First(predicate) or...
    public static implicit operator Expression<Func<T, bool>>(PredicateBuilder<T> predicate)
    {
        if (object.ReferenceEquals(predicate, null))
        {
            return null;
        }

        // Handling of empty PredicateBuilder
        Expression current = predicate.Current ?? Expression.Constant(true);

        Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(current, Parameter);
        return lambda;
    }

    public static implicit operator PredicateBuilder<T>(Expression<Func<T, bool>> expression)
    {
        var predicate = new PredicateBuilder<T>();

        if (expression != null)
        {
            // Equivalent to predicate.Or(expression)
            predicate.And(expression);
        }

        return predicate;
    }

    public void And(Expression<Func<T, bool>> expression)
    {
        if (expression == null)
        {
            throw new ArgumentNullException("expression");
        }

        var expression2 = new ParameterConverter(expression.Parameters[0], Parameter).Visit(expression.Body);
        this.Current = this.Current != null ? Expression.AndAlso(this.Current, expression2) : expression2;
    }

    public void Or(Expression<Func<T, bool>> expression)
    {
        if (expression == null)
        {
            throw new ArgumentNullException("expression");
        }

        var expression2 = new ParameterConverter(expression.Parameters[0], Parameter).Visit(expression.Body);
        this.Current = this.Current != null ? Expression.OrElse(this.Current, expression2) : expression2;
    }

    public override string ToString()
    {
        // We reuse the .ToString() of Expression<Func<T, bool>>
        // Implicit cast here :-)
        Expression<Func<T, bool>> expression = this;
        return expression.ToString();
    }

    // Small ExpressionVisitor that replaces the ParameterExpression of
    // an Expression with another ParameterExpression (to make two
    // Expressions "compatible")
    protected class ParameterConverter : ExpressionVisitor
    {
        public readonly ParameterExpression From;
        public readonly ParameterExpression To;

        public ParameterConverter(ParameterExpression from, ParameterExpression to)
        {
            this.From = from;
            this.To = to;
        }

        protected override Expression VisitParameter(ParameterExpression node)
        {
            if (node == this.From)
            {
                node = this.To;
            }

            return base.VisitParameter(node);
        }
    }
}

public static class PredicateBuilder
{
    // The value of source isn't really necessary/interesting. Its type
    // is :-) By passing a query you are building to Create, the compiler
    // will give to Create the the of the object returned from the query
    // Use it like:
    // var predicate = PredicateBuilder.Create<MyType>();
    // or
    // var predicate = PredicateBuilder.Create(query);
    public static PredicateBuilder<T> Create<T>(IEnumerable<T> source = null)
    {
        return new PredicateBuilder<T>();
    }

    // Useful if you want to start with a query:
    // var predicate = PredicateBuilder.Create<MyType>(x => x.ID != 0);
    // Note that if expression == null, then a new PredicateBuilder<T>()
    // will be returned (that by default is "true")
    public static PredicateBuilder<T> Create<T>(Expression<Func<T, bool>> expression)
    {
        // Implicit cast to PredicateBuilder<T>
        return expression;
    }
}

像这样使用它:

var predicate = PredicateBuilder.Create(query);

然后一切都是一样的(但删除LinqKit.Extensions.AsExpandable部分)

【讨论】:

  • 我希望 Any 在这种情况下产生完全相同的 SQL。
  • @MarcinJuraszek 可能,不是 100% 确定 :-) 我会给这种方式 50% 的工作机会。 50% 优于 0%。
  • OP 的查询失败了,因为它嵌套了 OR,而且它太深了。这是在做同样的事情,并且会因为同样的原因而失败。
  • @Servy 这里我没有实体框架数据库,30 分钟内没有人回复,所以有人给出“正确”“100% 工作”解决方案的机会越来越低。如果它不起作用,我会在上面写不起作用。这不是第一次在 SO 上发布无效的解决方案
  • Any() 生成一个由n 单行查询之间的联合组成的查询(其中n 是param 中的项目数)。这是一个快速达到嵌套限制的结构。带有ORs 的查询可以包含更多谓词。尽管如此,出于性能原因,param 中的项目数量不应“太高”,但现在限制为数千,而不是 ~50。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-08-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-10
  • 1970-01-01
相关资源
最近更新 更多