加入收藏 | 设为首页 | 会员中心 | 我要投稿 安卓应用网 (https://www.0791zz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 数据库 > MsSql > 正文

linq-to-sql – Linq to SQL抛出StackOverflowException

发布时间:2020-05-24 09:49:09 所属栏目:MsSql 来源:互联网
导读:我正在使用 Linq to SQL执行一个非常简单的查询.我正在创建表达式,然后将其传递给Where()扩展方法.当我尝试实际执行查询时,Linq内部正在抛出StackOverflowException.这是代码: int expectedCount = 4;ExpressionFuncThing, bool expression = ...;//Expressi

我正在使用 Linq to SQL执行一个非常简单的查询.我正在创建表达式,然后将其传递给Where()扩展方法.当我尝试实际执行查询时,Linq内部正在抛出StackOverflowException.这是代码:

int expectedCount = 4;
Expression<Func<Thing,bool>> expression = ...;

//Expression looks like (LocaleID = 1 && GenderID ==1 && (TimeFrameID == 2007 || TimeFrameID == 2008))

using (XYZDataContext context = new XYZDataContext())
{
    int count = context.Things.Where(expression).Count();
    //...
}

这是表达式的DebugView:

.Lambda #Lambda1<System.Func`2[XYZ.DataAccess.Thing,System.Boolean]>(XYZ.DataAccess.Thing $o)
{
    .Invoke (.Lambda #Lambda2<System.Func`2[XYZ.DataAccess.Thing,System.Boolean]>)($o) & .Invoke (.Lambda #Lambda3<System.Func`2[XYZ.DataAccess.Thing,System.Boolean]>)($o)
}

.Lambda #Lambda2<System.Func`2[XYZ.DataAccess.Thing,System.Boolean]>(XYZ.DataAccess.Thing $o)
{
    .Invoke (.Lambda #Lambda4<System.Func`2[XYZ.DataAccess.Thing,System.Boolean]>)($o) & .Invoke (.Lambda #Lambda5<System.Func`2[XYZ.DataAccess.Thing,System.Boolean]>)($o)
}

.Lambda #Lambda3<System.Func`2[XYZ.DataAccess.Thing,System.Boolean]>(XYZ.DataAccess.Thing $o)
{
    .Invoke (.Lambda #Lambda6<System.Func`2[XYZ.DataAccess.Thing,System.Boolean]>)($o) | .Invoke (.Lambda #Lambda7<System.Func`2[XYZ.DataAccess.Thing,System.Boolean]>)($o)
}

.Lambda #Lambda4<System.Func`2[XYZ.DataAccess.Thing,System.Boolean]>(XYZ.DataAccess.Thing $o)
{
    $o.LocaleID == .Constant<System.Nullable`1[System.Int32]>(1)
}

.Lambda #Lambda5<System.Func`2[XYZ.DataAccess.Thing,System.Boolean]>(XYZ.DataAccess.Thing $o)
{
    $o.GenderID == .Constant<System.Nullable`1[System.Int32]>(1)
}

.Lambda #Lambda6<System.Func`2[XYZ.DataAccess.Thing,System.Boolean]>(XYZ.DataAccess.Thing $o)
{
    $o.TimeframeID == .Constant<System.Nullable`1[System.Int32]>(2007)
}

.Lambda #Lambda7<System.Func`2[XYZ.DataAccess.Thing,System.Boolean]>(XYZ.DataAccess.Thing $o)
{
    $o.TimeframeID == .Constant<System.Nullable`1[System.Int32]>(2008)
}

这个表达对我来说是正确的,这是相当微不足道的.当我读取调试视图时,我看到:

((LocaleID == 1 && GenderID == 1) && (TimeFrameID == 2007 || TimeFrameID == 2008))

…哪个是对的.

更新1

删除其中一个内部或子句,它工作正常.因此,无论如何,同时使用内部或子句都会破坏从LINQ到SQL的转换.

更新2

我无法让调试器进入.NET Framework代码 – 我尝试使用Reflector和Visual Studio一样.我进去了一次,但总的来说,踩踏不起作用.我在StackOverflowException中遇到的一次是在:

ExecutionContext.Run(ExecutionContext executionContext,ContextCallback callback,object state,bool ignoreSyncCtx)

更新3

以下是用于创建Expression的代码.有太多的代码要发布,但它的核心在下面.我有一些类,允许我构建一个复杂的多级查询并将其序列化为JSON和XML.核心是,每个查询都是使用以下方法构建的,然后是Or’d和And’d:

public class LinqSearchField<T,V> : ISearchField
{
    public string Name { get; private set; }
    public Expression<Func<T,V>> Selector { get; private set; }

    public Expression<Func<T,bool>> LessThan(V value)
    {
        return Expression.Lambda<Func<T,bool>>(Expression.LessThan(this.Selector.Body,GetConstant(value)),this.Selector.Parameters);
    }

    public Expression<Func<T,bool>> LessThanOrEqual(V value)
    {
        return Expression.Lambda<Func<T,bool>>(Expression.LessThanOrEqual(this.Selector.Body,bool>> Equal(V value)
    {
        return Expression.Lambda<Func<T,bool>>(Expression.Equal(this.Selector.Body,bool>> NotEqual(V value)
    {
        return Expression.Lambda<Func<T,bool>>(Expression.NotEqual(this.Selector.Body,bool>> GreaterThan(V value)
    {
        return Expression.Lambda<Func<T,bool>>(Expression.GreaterThan(this.Selector.Body,bool>> GreaterThanOrEqual(V value)
    {
        return Expression.Lambda<Func<T,bool>>(Expression.GreaterThanOrEqual(this.Selector.Body,this.Selector.Parameters);
    }

    private ConstantExpression GetConstant(V value)
    {
        return Expression.Constant(value,typeof(V));
    }

    public Expression<Func<T,bool>> Null()
    {
        return Expression.Lambda<Func<T,Expression.Constant(null)),bool>> NotNull()
    {
        return Expression.Lambda<Func<T,this.Selector.Parameters);
    }
}

这是和代码(OR代码是相同的,但使用Expression.And代替):

public static Expression<Func<T,bool>> And<T>(this Expression<Func<T,bool>> expression1,Expression<Func<T,bool>> expression2)
{
    ParameterExpression[] parameters = expression1.Parameters.Union(expression2.Parameters).Distinct(new ParameterExpressionComparer()).ToArray();
    InvocationExpression invocationExpression1 = Expression.Invoke(expression1,parameters);
    InvocationExpression invocationExpression2 = Expression.Invoke(expression2,parameters);
    Expression binaryExpression = null;

    //And the current expression to the previous one.
    binaryExpression = Expression.AndAlso(invocationExpression1,invocationExpression2); //Or OrElse.

    //Wrap the expression in a lambda.
    return Expression.Lambda<Func<T,bool>>(binaryExpression,parameters);
}

更新4

它可能会不受欢迎,但这是一个sample which reproduces this issue.我真的需要弄清楚这里发生了什么.

解决方法

我最初有怀疑,但现在可以确认.

你正在组合两个lambda,它们有两个完全不同的参数实例.参数实例不可交换,即使它们具有相同的名称和相同的类型.它们是不同范围内的有效参数.当您尝试使用错误的参数对象调用其中一个表达式时,会出现混乱,在这种情况下,堆栈溢出.

你应该做的是创建一个新的参数实例(或重用一个)并重新绑定lambda的主体以使用该新参数.我怀疑这会解决这个问题.为了更进一步,你应该通过重建它们来正确地组合这些表达式,而不是将它们作为方法调用一起修补.我怀疑查询提供程序会以任何方式将这些视为调用.

尝试使用And()和Or()方法的这个实现以及此辅助方法来进行重新绑定:

public static Expression<Func<T,bool>> expression2)
{
    // reuse the first expression's parameter
    var param = expression1.Parameters.Single();
    var left = expression1.Body;
    var right = RebindParameter(expression2.Body,expression2.Parameters.Single(),param);
    var body = Expression.AndAlso(left,right);
    return Expression.Lambda<Func<T,bool>>(body,param);
}

public static Expression<Func<T,bool>> Or<T>(this Expression<Func<T,bool>> expression2)
{
    var param = expression1.Parameters.Single();
    var left = expression1.Body;
    var right = RebindParameter(expression2.Body,param);
    var body = Expression.OrElse(left,param);
}

private static Expression RebindParameter(Expression expr,ParameterExpression oldParam,ParameterExpression newParam)
{
    switch (expr.NodeType)
    {
    case ExpressionType.Parameter:
        var asParameterExpression = expr as ParameterExpression;
        return (asParameterExpression.Name == oldParam.Name)
            ? newParam
            : asParameterExpression;
    case ExpressionType.MemberAccess:
        var asMemberExpression = expr as MemberExpression;
        return asMemberExpression.Update(
            RebindParameter(asMemberExpression.Expression,oldParam,newParam));
    case ExpressionType.AndAlso:
    case ExpressionType.OrElse:
    case ExpressionType.Equal:
    case ExpressionType.NotEqual:
    case ExpressionType.LessThan:
    case ExpressionType.LessThanOrEqual:
    case ExpressionType.GreaterThan:
    case ExpressionType.GreaterThanOrEqual:
        var asBinaryExpression = expr as BinaryExpression;
        return asBinaryExpression.Update(
            RebindParameter(asBinaryExpression.Left,newParam),asBinaryExpression.Conversion,RebindParameter(asBinaryExpression.Right,newParam));
    case ExpressionType.Call:
        var asMethodCallExpression = expr as MethodCallExpression;
        return asMethodCallExpression.Update(
            RebindParameter(asMethodCallExpression.Object,asMethodCallExpression.Arguments.Select(arg =>
                RebindParameter(arg,newParam)));
    case ExpressionType.Invoke:
        var asInvocationExpression = expr as InvocationExpression;
        return asInvocationExpression.Update(
            RebindParameter(asInvocationExpression.Expression,asInvocationExpression.Arguments.Select(arg =>
                RebindParameter(arg,newParam)));
    case ExpressionType.Lambda:
        var asLambdaExpression = expr as LambdaExpression;
        return Expression.Lambda(
            RebindParameter(asLambdaExpression.Body,asLambdaExpression.Parameters.Select(param =>
                (ParameterExpression)RebindParameter(param,newParam)));
    default:
        // you should add cases for any expression types that have subexpressions
        return expr;
    }
}

(编辑:安卓应用网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读