#linq #linq-to-nhibernate #predicatebuilder
#linq #linq-to-nhibernate #predicatebuilder
Вопрос:
У меня проблема. Я получаю сообщение «Элемент с тем же ключом уже добавлен». исключение, когда я пытаюсь перечислить результаты запроса. Это происходит, когда я пытаюсь включить выражение из той же исходной переменной в окончательный запрос. Я пытался обойти это, скопировав выражение, но безрезультатно:
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."
Я использую Linq для Nhibernate. Вот трассировка стека. Кто-нибудь может это объяснить?
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:CSharpNHNHnhibernatesrcNHibernateLinqVisitorsExpressionParameterVisitor.cs:line 43
at Remotion.Linq.Parsing.ExpressionTreeVisitor.VisitExpression(Expression expression) in :line 0
at NHibernate.Linq.Visitors.NhExpressionTreeVisitor.VisitExpression(Expression expression) in d:CSharpNHNHnhibernatesrcNHibernateLinqVisitorsNhExpressionTreeVisitor.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:CSharpNHNHnhibernatesrcNHibernateLinqVisitorsNhExpressionTreeVisitor.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:CSharpNHNHnhibernatesrcNHibernateLinqVisitorsNhExpressionTreeVisitor.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:CSharpNHNHnhibernatesrcNHibernateLinqVisitorsNhExpressionTreeVisitor.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:CSharpNHNHnhibernatesrcNHibernateLinqVisitorsNhExpressionTreeVisitor.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:CSharpNHNHnhibernatesrcNHibernateLinqVisitorsNhExpressionTreeVisitor.cs:line 32
at NHibernate.Linq.Visitors.ExpressionParameterVisitor.Visit(Expression expression) in d:CSharpNHNHnhibernatesrcNHibernateLinqVisitorsExpressionParameterVisitor.cs:line 21
at NHibernate.Linq.NhLinqExpression..ctor(Expression expression) in d:CSharpNHNHnhibernatesrcNHibernateLinqNhLinqExpression.cs:line 38
at NHibernate.Linq.DefaultQueryProvider.PrepareQuery(Expression expression, IQueryamp; query, NhLinqExpressionamp; nhQuery) in d:CSharpNHNHnhibernatesrcNHibernateLinqDefaultQueryProvider.cs:line 67
at NHibernate.Linq.DefaultQueryProvider.Execute(Expression expression) in d:CSharpNHNHnhibernatesrcNHibernateLinqDefaultQueryProvider.cs:line 33
at NHibernate.Linq.DefaultQueryProvider.Execute[TResult](Expression expression) in d:CSharpNHNHnhibernatesrcNHibernateLinqDefaultQueryProvider.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:UsersIsaac.GDesktopcheckoutField SystemFieldSystemFieldSystemCoreReportsObservationReportObservationReportSearch.cs:line 39
at FieldSystemGUI.Controls.Reports.ObservationReport.ObservationReport_ReportSearchEvent(Object sender, DoWorkEventArgs e) in C:UsersIsaac.GDesktopcheckoutField SystemFieldSystemFieldSystemGUIControlsReportsObservationReport.cs:line 99
at GUIComponents.Controls.Reports.BaseCommonReport.backgroundWorker_DoWork(Object sender, DoWorkEventArgs e) in C:UsersIsaac.GDesktopcheckoutLibrary ProjectsGUIComponentsGUIComponentsControlsReportsBaseCommonReport.cs:line 194
at System.ComponentModel.BackgroundWorker.OnDoWork(DoWorkEventArgs e)
at System.ComponentModel.BackgroundWorker.WorkerThreadStart(Object argument)
Комментарии:
1. Какое исключение вы получаете?
2. «Элемент с тем же ключом уже добавлен». Система. Я считаю, что ArgumentsException
Ответ №1:
Я только что попробовал ваш пример в LINQ to Objects, LINQ to SQL и LINQ to Entities, и он отлично работает во всех из них (с небольшими изменениями). Возможно, вы захотите сообщить об этом как об ошибке команде LINQ to NHibernate.
Чтобы «скопировать» дерево выражений, вам нужно будет скопировать каждый узел этого дерева, используя что-то вроде an ExpressionVisitor
, что является громоздким и может или не может в конечном итоге решить вашу проблему.
В зависимости от того, как структурирован ваш код, одним из обходных путей может быть воспроизведение исходного дерева выражений дважды, передавая все, что создает это выражение в первую очередь:
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();
Обновить
Я просто потратил некоторое время на чтение ExpressionVisitors, и, похоже, это может быть не так уж сложно. Посмотрите, работает ли это:
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();
Комментарии:
1. Я попробую это. Спасибо. Я потратил часы, пытаясь выяснить, что я делаю неправильно.
2. Обходной путь сработал. Я бы не подумал сделать это без вас! Я предполагаю, что метод копирования, который я использую, не является «истинной» копией.
3. В вашем обновлении: трюк с изменением не сработал. Большое спасибо за все время, которое вы тратите на эту раздражающую проблему
Ответ №2:
Вот предположение с большой вероятностью.
Вы можете добавить это в свою копию класса predicatebuilder
public static Expression<Func<T, bool>> Copy<T>
(this Expression<Func<T, bool>> expr1)
{
return Expression.Lambda<Func<T, bool>>(expr1.Body, expr1.Parameters);
}
А затем используйте его как:
predicate3 = predicate3.And(predicate1.Copy());
Вероятно, это не решит вашу проблему с дублирующимся ключом — я не вижу никаких задействованных словарей. Против чего вы запрашиваете?
Комментарии:
1. Я только что попробовал это на самом деле. Это не работает. Я все еще получаю исключение. Это очень неприятно, потому что я не знаю, что такое дублирующий ключ. Кажется, это происходит, когда я пытаюсь дважды добавить одну и ту же переменную предиката.