c# – 带有“ANY”的子查询和本地数组生成嵌套太深的SQL语句

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语句的某些部分嵌套得太深.重写查询或将其分解为较小的查询.”

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

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

谢谢

最佳答案 您可以尝试动态构建查询:

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,我将附加我的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部分)

点赞