问题
I have a problem. I'm getting an "An item with the same key has already been added." exception when I try to enumerate over the query results. It happens when I try to include an expression from the same original variable in the final query. I tried to get around this by copying the expression but to no avail:
var predicate1 = PredicateBuilder.True<SomeType>();
var predicate2 = PredicateBuilder.True<SomeType>();
var predicate3 = PredicateBuilder.True<SomeType>();
System.Linq.Expressions.Expression<Func<FieldObservation, bool>> predicate1copy1 = System.Linq.Expressions.Expression.Lambda<Func<FieldObservation, bool>>(predicate1.Body, predicate1.Parameters);
System.Linq.Expressions.Expression<Func<FieldObservation, bool>> predicate1copy2 = System.Linq.Expressions.Expression.Lambda<Func<FieldObservation, bool>>(predicate1.Body, predicate1.Parameters);
predicate2 = x => x.FirstBoolProperty;
predicate2 = predicate2.And(predicate1copy1);
predicate3 = x => x.SecondBoolProperty;
predicate3 = predicate3.And(predicate1copy2); //predicate1copy2 comes from the same original predicate1
var predicate4 = predicate2.Or(predicate3);
var results1 = query.Where(predicate4).ToList(); //exception thrown here: "An item with the same key has already been added."
I'm using Linq to Nhibernate. Here is the stacktrace. Can anyone explain this?
Stacktrace:
at System.ThrowHelper.ThrowArgumentException(ExceptionResource resource)
at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
at System.Collections.Generic.Dictionary`2.Add(TKey key, TValue value)
at NHibernate.Linq.Visitors.ExpressionParameterVisitor.VisitConstantExpression(ConstantExpression expression) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Linq\Visitors\ExpressionParameterVisitor.cs:line 43
at Remotion.Linq.Parsing.ExpressionTreeVisitor.VisitExpression(Expression expression) in :line 0
at NHibernate.Linq.Visitors.NhExpressionTreeVisitor.VisitExpression(Expression expression) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Linq\Visitors\NhExpressionTreeVisitor.cs:line 32
at Remotion.Linq.Parsing.ExpressionTreeVisitor.VisitBinaryExpression(BinaryExpression expression) in :line 0
at Remotion.Linq.Parsing.ExpressionTreeVisitor.VisitExpression(Expression expression) in :line 0
at NHibernate.Linq.Visitors.NhExpressionTreeVisitor.VisitExpression(Expression expression) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Linq\Visitors\NhExpressionTreeVisitor.cs:line 32
at Remotion.Linq.Parsing.ExpressionTreeVisitor.VisitBinaryExpression(BinaryExpression expression) in :line 0
at Remotion.Linq.Parsing.ExpressionTreeVisitor.VisitExpression(Expression expression) in :line 0
at NHibernate.Linq.Visitors.NhExpressionTreeVisitor.VisitExpression(Expression expression) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Linq\Visitors\NhExpressionTreeVisitor.cs:line 32
at Remotion.Linq.Parsing.ExpressionTreeVisitor.VisitLambdaExpression(LambdaExpression expression) in :line 0
at Remotion.Linq.Parsing.ExpressionTreeVisitor.VisitExpression(Expression expression) in :line 0
at NHibernate.Linq.Visitors.NhExpressionTreeVisitor.VisitExpression(Expression expression) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Linq\Visitors\NhExpressionTreeVisitor.cs:line 32
at Remotion.Linq.Parsing.ExpressionTreeVisitor.VisitUnaryExpression(UnaryExpression expression) in :line 0
at Remotion.Linq.Parsing.ExpressionTreeVisitor.VisitExpression(Expression expression) in :line 0
at NHibernate.Linq.Visitors.NhExpressionTreeVisitor.VisitExpression(Expression expression) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Linq\Visitors\NhExpressionTreeVisitor.cs:line 32
at Remotion.Linq.Parsing.ExpressionTreeVisitor.VisitAndConvert[T](T expression, String methodName) in :line 0
at Remotion.Linq.Parsing.ExpressionTreeVisitor.<>c__DisplayClass6`1.<VisitAndConvert>b__5(T expression) in :line 0
at Remotion.Linq.Parsing.ExpressionTreeVisitor.VisitList[T](ReadOnlyCollection`1 list, Func`2 visitMethod) in :line 0
at Remotion.Linq.Parsing.ExpressionTreeVisitor.VisitAndConvert[T](ReadOnlyCollection`1 expressions, String callerName) in :line 0
at Remotion.Linq.Parsing.ExpressionTreeVisitor.VisitMethodCallExpression(MethodCallExpression expression) in :line 0
at Remotion.Linq.Parsing.ExpressionTreeVisitor.VisitExpression(Expression expression) in :line 0
at NHibernate.Linq.Visitors.NhExpressionTreeVisitor.VisitExpression(Expression expression) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Linq\Visitors\NhExpressionTreeVisitor.cs:line 32
at NHibernate.Linq.Visitors.ExpressionParameterVisitor.Visit(Expression expression) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Linq\Visitors\ExpressionParameterVisitor.cs:line 21
at NHibernate.Linq.NhLinqExpression..ctor(Expression expression) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Linq\NhLinqExpression.cs:line 38
at NHibernate.Linq.DefaultQueryProvider.PrepareQuery(Expression expression, IQuery& query, NhLinqExpression& nhQuery) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Linq\DefaultQueryProvider.cs:line 67
at NHibernate.Linq.DefaultQueryProvider.Execute(Expression expression) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Linq\DefaultQueryProvider.cs:line 33
at NHibernate.Linq.DefaultQueryProvider.Execute[TResult](Expression expression) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Linq\DefaultQueryProvider.cs:line 40
at Remotion.Linq.QueryableBase`1.GetEnumerator() in :line 0
at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
at FieldSystemCore.Reports.ObservationReport.ObservationReportSearch.getResults(ObservationReportConfigurationObject config) in C:\Users\Isaac.G\Desktop\checkout\Field System\FieldSystem\FieldSystemCore\Reports\ObservationReport\ObservationReportSearch.cs:line 39
at FieldSystemGUI.Controls.Reports.ObservationReport.ObservationReport_ReportSearchEvent(Object sender, DoWorkEventArgs e) in C:\Users\Isaac.G\Desktop\checkout\Field System\FieldSystem\FieldSystemGUI\Controls\Reports\ObservationReport.cs:line 99
at GUIComponents.Controls.Reports.BaseCommonReport.backgroundWorker_DoWork(Object sender, DoWorkEventArgs e) in C:\Users\Isaac.G\Desktop\checkout\Library Projects\GUIComponents\GUIComponents\Controls\Reports\BaseCommonReport.cs:line 194
at System.ComponentModel.BackgroundWorker.OnDoWork(DoWorkEventArgs e)
at System.ComponentModel.BackgroundWorker.WorkerThreadStart(Object argument)
回答1:
I just tried your example in LINQ to Objects, LINQ to SQL, and LINQ to Entities, and it works fine in all of them (with minor tweaking). You may want to report it as a bug to the LINQ to NHibernate team.
In order to "copy" the expression tree, you'd have to copy every node of that tree using something like an ExpressionVisitor, which is cumbersome and may or may not end up fixing your problem.
Depending on how your code is structured, one workaround may be to reproduce the original expression tree twice, by passing in whatever produces that expression in the first place:
Func<Expression<Func<SomeType, bool>>> predicate1Builder =
() => PredicateBuilder.True<SomeType>();
var predicate2 = PredicateBuilder.True<SomeType>();
var predicate3 = PredicateBuilder.True<SomeType>();
predicate2 = x => x.FirstBoolProperty;
predicate2 = predicate2.And(predicate1Builder());
predicate3 = x => x.SecondBoolProperty;
predicate3 = predicate3.And(predicate1Builder());
var predicate4 = predicate2.Or(predicate3);
var results1 = query.Where(predicate4).ToList();
Update
I just spent some time reading up on ExpressionVisitors, and it looks like it might not be that difficult after all. See if this works:
public class Visitor : ExpressionVisitor
{
public Expression<T> Modify<T>(Expression<T> node) {return (Expression<T>)Visit(node);}
}
var predicate1 = PredicateBuilder.True<SomeType>();
var predicate2 = PredicateBuilder.True<SomeType>();
var predicate3 = PredicateBuilder.True<SomeType>();
predicate2 = x => x.FirstBoolProperty;
predicate2 = predicate2.And(predicate1);
predicate3 = x => x.SecondBoolProperty;
var copy = new Visitor().Modify(predicate1);
predicate3 = predicate3.And(copy);
var predicate4 = predicate2.Or(predicate3);
var results1 = query.Where(predicate4).ToList();
回答2:
Here's a longshot guess.
You can add this to your copy of the predicatebuilder class
public static Expression<Func<T, bool>> Copy<T>
(this Expression<Func<T, bool>> expr1)
{
return Expression.Lambda<Func<T, bool>>(expr1.Body, expr1.Parameters);
}
And then use it like:
predicate3 = predicate3.And(predicate1.Copy());
Probably won't solve your duplicate key problem - I don't see any Dictionaries involved. What are you querying against?
来源:https://stackoverflow.com/questions/7843832/why-am-i-getting-this-linq-to-nhibernate-exception-when-i-add-the-same-expressio