Why am I getting this Linq to Nhibernate exception when I add the same expression to the query twice?

坚强是说给别人听的谎言 提交于 2019-12-11 17:48:55

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!