C#:编译表达式时,已添加具有相同键的项

C#:编译表达式时,已添加具有相同键的项,c#,exception,lambda,expression,expression-trees,C#,Exception,Lambda,Expression,Expression Trees,好的,这里有一个棘手的问题。希望这里有一位表情大师,他能发现我做错了什么,因为我只是不明白 我正在构建用于过滤查询的表达式。为了简化这个过程,我有两个Expression扩展方法,使我的代码更干净,到目前为止,它们工作得很好。我已经为他们写了测试,除了一个,我今天写了一个。该测试完全失败,出现了一个长堆栈跟踪的ArgumentException。我就是不明白。特别是因为我已经在我的查询中成功地使用了该方法一段时间 无论如何,下面是我在运行测试时得到的堆栈跟踪: failed: System.Ar

好的,这里有一个棘手的问题。希望这里有一位表情大师,他能发现我做错了什么,因为我只是不明白

我正在构建用于过滤查询的表达式。为了简化这个过程,我有两个
Expression
扩展方法,使我的代码更干净,到目前为止,它们工作得很好。我已经为他们写了测试,除了一个,我今天写了一个。该测试完全失败,出现了一个长堆栈跟踪的
ArgumentException
。我就是不明白。特别是因为我已经在我的查询中成功地使用了该方法一段时间

无论如何,下面是我在运行测试时得到的堆栈跟踪:

failed: System.ArgumentException : An item with the same key has already been added.
    at System.ThrowHelper.ThrowArgumentException(ExceptionResource resource)
    at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
    at System.Linq.Expressions.ExpressionCompiler.PrepareInitLocal(ILGenerator gen, ParameterExpression p)
    at System.Linq.Expressions.ExpressionCompiler.GenerateInvoke(ILGenerator gen, InvocationExpression invoke, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.Generate(ILGenerator gen, Expression node, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.GenerateBinary(ILGenerator gen, BinaryExpression b, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.Generate(ILGenerator gen, Expression node, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.GenerateUnliftedAndAlso(ILGenerator gen, BinaryExpression b)
    at System.Linq.Expressions.ExpressionCompiler.GenerateAndAlso(ILGenerator gen, BinaryExpression b, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.GenerateBinary(ILGenerator gen, BinaryExpression b, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.Generate(ILGenerator gen, Expression node, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.GenerateUnliftedOrElse(ILGenerator gen, BinaryExpression b)
    at System.Linq.Expressions.ExpressionCompiler.GenerateOrElse(ILGenerator gen, BinaryExpression b, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.GenerateBinary(ILGenerator gen, BinaryExpression b, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.Generate(ILGenerator gen, Expression node, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.GenerateInvoke(ILGenerator gen, InvocationExpression invoke, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.Generate(ILGenerator gen, Expression node, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.GenerateUnliftedAndAlso(ILGenerator gen, BinaryExpression b)
    at System.Linq.Expressions.ExpressionCompiler.GenerateAndAlso(ILGenerator gen, BinaryExpression b, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.GenerateBinary(ILGenerator gen, BinaryExpression b, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.Generate(ILGenerator gen, Expression node, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.GenerateLambda(LambdaExpression lambda)
    at System.Linq.Expressions.ExpressionCompiler.CompileDynamicLambda(LambdaExpression lambda)
    at System.Linq.Expressions.Expression`1.Compile()
    PredicateTests.cs(257,0): at Namespace.ExpressionExtensionsTests.WhereWithin_CollectionIsFilteredAsExpected()
测试本身如下所示,在Compile语句中失败:

[Test]
public void WhereWithin_CollectionIsFilteredAsExpected()
{
    var range = new[] { Range.Create(2, 7), Range.Create(15, 18) };

    var predicate = Predicate
        .Create<int>(x => x % 2 == 0)
        .AndWithin(range, x => x)
        .Compile();

    var actual = Enumerable.Range(0, 20)
        .Where(predicate)
        .ToArray();

    Assert.That(actual, Is.EqualTo(new[] { 2, 4, 6, 16, 18 }));
}
[测试]
公共无效,在此范围内\u CollectionsFilteredesExpected()
{
var range=new[]{range.Create(2,7),range.Create(15,18)};
变量谓词=谓词
.Create(x=>x%2==0)
.AndWithin(范围,x=>x)
.Compile();
var实际值=可枚举范围(0,20)
.Where(谓词)
.ToArray();
断言(实际的,是.EqualTo(新的[]{2,4,6,16,18}));
}
我只是不明白错误信息。我认为这可能与我总是使用
x
作为参数名有关,但当我尝试交换它们时,似乎没有帮助。更让我感到奇怪的是,我已经在更大的Linq2Sql查询中使用了这个精确的方法一段时间,而且它们一直工作得很好。因此,在我的测试中,我尝试不编译表达式,而是使用
AsQueryable
,这样我就可以在上面使用它了。但这只是使异常发生在
ToArray
上。这是怎么回事?我怎样才能解决这个问题

您可以在以下行的zip文件中找到令人讨厌的代码:


注意:我在这里发布了一些相关的代码,但经过一些评论后,我决定将代码提取到它自己的项目中,从而更清楚地显示异常。更重要的是,它可以运行、编译和调试


更新:使用@Mark的一些建议进一步简化了示例项目。比如删除range类,而只是硬编码单个常量range。还添加了另一个示例,其中使用完全相同的方法实际上效果很好。因此,使用AndWithin方法会使应用程序崩溃,而使用WhereWithin方法实际上效果很好。我觉得自己很无知

  • (更新)

    • 这不是一个答案,但我希望它能帮助人们找到答案。我进一步简化了代码,使其仅为一个文件,并且仍然以相同的方式失败。我已经重命名了变量,所以“x”不会被使用两次。我删除了Range类,并用硬编码的常量0和1替换它

      using System;
      using System.Linq;
      using System.Linq.Expressions;
      
      class Program
      {
          static Expression<Func<int, bool>> And(Expression<Func<int, bool>> first,
                                                 Expression<Func<int, bool>> second)
          {
              var x = Expression.Parameter(typeof(int), "x");
              var body = Expression.AndAlso(Expression.Invoke(first, x), Expression.Invoke(second, x));
              return Expression.Lambda<Func<int, bool>>(body, x);
          }
      
          static Expression<Func<int, bool>> GetPredicateFor(Expression<Func<int, int>> selector)
          {
              var param = Expression.Parameter(typeof(int), "y");
              var member = Expression.Invoke(selector, param);
      
              Expression body =
                  Expression.AndAlso(
                      Expression.GreaterThanOrEqual(member, Expression.Constant(0, typeof(int))),
                      Expression.LessThanOrEqual(member, Expression.Constant(1, typeof(int))));
      
              return Expression.Lambda<Func<int, bool>>(body, param);
          }
      
          static void Main()
          {
              Expression<Func<int, bool>> predicate = a => true;
              predicate = And(predicate, GetPredicateFor(b => b)); // Comment out this line and it will run without error
              var z = predicate.Compile();
          }
      }
      
      使用系统;
      使用System.Linq;
      使用System.Linq.Expressions;
      班级计划
      {
      静态表达式和(表达式优先,
      表达(第二)
      {
      var x=表达式参数(typeof(int),“x”);
      var body=Expression.AndAlso(Expression.Invoke(第一个,x),Expression.Invoke(第二个,x));
      返回表达式.Lambda(body,x);
      }
      静态表达式GetPredicateFor(表达式选择器)
      {
      var param=表达式参数(typeof(int),“y”);
      var member=Expression.Invoke(选择器,参数);
      表达式体=
      安达尔索(
      Expression.GreaterThanOrEqual(成员,Expression.Constant(0,typeof(int)),
      Expression.LessThanOrEqual(成员,表达式.常量(1,typeof(int)));
      返回表达式.Lambda(body,param);
      }
      静态void Main()
      {
      表达式谓词=a=>true;
      predicate=And(predicate,GetPredicateFor(b=>b));//注释掉这一行,它将无误地运行
      var z=predicate.Compile();
      }
      }
      
      该表达式在调试器中如下所示:

      x => (Invoke(a => True,x) && Invoke(y => ((Invoke(b => b,y) >= 0) && (Invoke(b => b,y) <= 1)),x))
      

      x=>(Invoke(a=>True,x)&&Invoke(y=>)((Invoke(b=>b,y)>=0)&&&(Invoke(b=>b,y)我对您的方法进行了一些重构,以使编译器更愉快:

      public static Expression<Func<TSubject, bool>> AndWithin<TSubject, TField>(
          this Expression<Func<TSubject, bool>> original,
          IEnumerable<Range<TField>> range, Expression<Func<TSubject, TField>> field) where TField : IComparable<TField>
      {
        return original.And(range.GetPredicateFor(field));
      }
      
      
      static Expression<Func<TSource, bool>> GetPredicateFor<TSource, TValue>
          (this IEnumerable<Range<TValue>> range, Expression<Func<TSource, TValue>> selector) where TValue : IComparable<TValue>
      {
        var param = Expression.Parameter(typeof(TSource), "x");
      
        if (range == null || !range.Any())
          return Expression.Lambda<Func<TSource, bool>>(Expression.Constant(false), param);
      
        Expression body = null;
        foreach (var r in range)
        {
          Expression<Func<TValue, TValue, TValue, bool>> BT = (val, min, max) => val.CompareTo(min) >= 0 && val.CompareTo(max) <= 0;
          var newPart = Expression.Invoke(BT, param,
                                            Expression.Constant(r.Start, typeof(TValue)),
                                            Expression.Constant(r.End, typeof(TValue)));
      
          body = body == null ? newPart : (Expression)Expression.OrElse(body, newPart);
        }
      
        return Expression.Lambda<Func<TSource, bool>>(body, param);
      }
      
      公共静态表达式和内部(
      这句话原汁原味,,
      IEnumerable范围,表达式字段),其中TField:IComparable
      {
      返回原始的.And(range.GetPredicateFor(field));
      }
      静态表达式GetPredicateFor
      (此IEnumerable范围,表达式选择器)其中TValue:IComparable
      {
      var param=表达式参数(typeof(TSource),“x”);
      if(range==null | |!range.Any())
      返回表达式.Lambda(表达式.Constant(false),param);
      表达式体=null;
      foreach(范围内的var r)
      {
      
      表达式BT=(val,min,max)=>val.CompareTo(min)>=0&&val.CompareTo(max)
      Range
      是您自己编写的类吗?@Mark:是的,它主要只是一个有开始和结束的类。虽然我有一系列扩展方法,它被覆盖了等号等等。但在这种情况下,这并不重要。它是一个非常简单的类:)作为注释,System.Predicate已经存在,因此命名有点混乱。@recursive:我知道,但找不到更好的。而且因为我从来没有真正使用过该谓词委托……是的……:p@Mark:好的,我不能不做就睡觉。做了一个小项目,用最少的代码来出错。好吧,可能是co甚至更少,但是是的。编辑问题用一些简化创建了一个更新的项目。请检查:)注意简化的代码(只有6行的版本)在.NET 4.0中工作,但在.NET 3.5中不工作,这对我来说表明.NET 3.5中有一个bug已在4.0中修复。此外,原始代码在.NET 4.0中不工作
      public static Expression<Func<TSubject, bool>> AndWithin<TSubject, TField>(
          this Expression<Func<TSubject, bool>> original,
          IEnumerable<Range<TField>> range, Expression<Func<TSubject, TField>> field) where TField : IComparable<TField>
      {
        return original.And(range.GetPredicateFor(field));
      }
      
      
      static Expression<Func<TSource, bool>> GetPredicateFor<TSource, TValue>
          (this IEnumerable<Range<TValue>> range, Expression<Func<TSource, TValue>> selector) where TValue : IComparable<TValue>
      {
        var param = Expression.Parameter(typeof(TSource), "x");
      
        if (range == null || !range.Any())
          return Expression.Lambda<Func<TSource, bool>>(Expression.Constant(false), param);
      
        Expression body = null;
        foreach (var r in range)
        {
          Expression<Func<TValue, TValue, TValue, bool>> BT = (val, min, max) => val.CompareTo(min) >= 0 && val.CompareTo(max) <= 0;
          var newPart = Expression.Invoke(BT, param,
                                            Expression.Constant(r.Start, typeof(TValue)),
                                            Expression.Constant(r.End, typeof(TValue)));
      
          body = body == null ? newPart : (Expression)Expression.OrElse(body, newPart);
        }
      
        return Expression.Lambda<Func<TSource, bool>>(body, param);
      }