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);
}