【问题标题】:Convert/Embed one expression tree into another将一个表达式树转换/嵌入到另一个
【发布时间】:2012-06-27 23:49:47
【问题描述】:

编辑:更详细的问题。

我正在处理 nHibernate 中的批处理操作,特别是针对 In 查询以克服 SQL Server 中的 2100 参数限制大小。

为此,我用这个构造函数创建了一个类(这是一个非常简化的版本):

BatchedQuery(session.Query<Foo>(), allValues, (l, e) => l.Contains(e.Id));

...

public BatchedQuery(IQueryable<TEntity> query, IList<TValue> allValues, Expression<Func<IList<TValue>, TEntity, bool>> predicate)
{
   List<TValue> values = ...; // Select a batch from allValues
   ...

   // I want to pass the values to the expression passed in...
   // something like this, without using Compile: 
   // e => predicate.Compile()(values, e)

   // using JKor's method, I tried this...
   ParameterExpression param = Expression.Parameter(typeof(TEntity), "e");
   Expression<Func<TEntity, bool>> expr2 =
       Expression.Lambda<Func<TEntity, bool>>(Expression.Invoke(predicate,
           Expression.Constant(batchOfValues), param), param);

   query = query.Where(expr2);

   // Do something with the query...
}

// Somewhere else..
// This causes the exception
batchedQuery.ToList();

上述情况导致 nHibernate 抛出 KeyNotFoundException

这是堆栈跟踪:

at System.Collections.Generic.Dictionary`2.get_Item(TKey key)
at NHibernate.Param.NamedParameterSpecification.SetEffectiveType(QueryParameters queryParameters)
at NHibernate.Param.ParametersBackTrackExtensions.ResetEffectiveExpectedType(IEnumerable`1 parameterSpecs, QueryParameters queryParameters)
at NHibernate.Hql.Ast.ANTLR.Loader.QueryLoader.ResetEffectiveExpectedType(IEnumerable`1 parameterSpecs, QueryParameters queryParameters)
at NHibernate.Loader.Loader.CreateSqlCommand(QueryParameters queryParameters, ISessionImplementor session)
at NHibernate.Impl.MultiQueryImpl.AggregateQueriesInformation()
at NHibernate.Impl.MultiQueryImpl.get_Parameters()
at NHibernate.Impl.MultiQueryImpl.CreateCombinedQueryParameters()
at NHibernate.Impl.MultiQueryImpl.List()
at NHibernate.Impl.FutureQueryBatch.GetResultsFrom(IMultiQuery multiApproach)
at NHibernate.Impl.FutureBatch`2.GetResults()
at NHibernate.Impl.FutureBatch`2.get_Results()
at NHibernate.Impl.FutureBatch`2.GetCurrentResult[TResult](Int32 currentIndex)
at NHibernate.Impl.FutureBatch`2.<>c__DisplayClass4`1.<GetEnumerator>b__3()
at NHibernate.Impl.DelayedEnumerator`1.<get_Enumerable>d__0.MoveNext()
at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
at NovusERP.Data.Helpers.BatchedQuery`2.ToList() in D:\Short Utilities\Novus\NovusERP\NovusERP.Data\Helpers\BatchedQuery.cs:line 63
at NovusERP.Modules.Payroll.Attendance.AttendanceViewModel.GetEmployees(IList`1 selectedEmployeeIds) in D:\Short Utilities\Novus\NovusERP\NovusERP.Modules\Payroll\Attendance\AttendanceViewModel.cs:line 79
at NovusERP.Modules.Payroll.Attendance.AttendanceViewModel..ctor(MonthYear currentMonth, IList`1 selectedEmployeeIds) in D:\Short Utilities\Novus\NovusERP\NovusERP.Modules\Payroll\Attendance\AttendanceViewModel.cs:line 47
at NovusERP.Modules.Payroll.Attendance.AttendanceView..ctor(MonthYear currentMonth, IList`1 selectedEmployees) in D:\Short Utilities\Novus\NovusERP\NovusERP.Modules\Payroll\Attendance\AttendanceView.xaml.cs:line 18
at lambda_method(Closure , Object[] )
at Autofac.Core.Activators.Reflection.ConstructorParameterBinding.Instantiate()

谁能指出我正确的方向?任何帮助将不胜感激。

问候,
尤格什。

【问题讨论】:

    标签: c# nhibernate lambda expression-trees iqueryable


    【解决方案1】:

    如果你想在不编译 expr1 的情况下生成 expr2,你不能只使用内置的编译器转换。这就是你想要的:

    Expression<Func<IList<TValue>, TEntity, bool>> expr1 = (l, e) => l.Contains(e.Id);
    ParameterExpression param = Expression.Parameter(typeof(TEntity), "e");
    Expresssion<Func<TEntity, bool>> expr2 = Expression.Lambda<Func<TEntity, bool>>(Expression.Invoke(expr1, Expression.Constant(values), param), param);
    

    【讨论】:

    • 谢谢。让我走一点。有什么办法,我可以在原始 expr? I mean make expr2 without calling Invoke 中替换 l 吗?
    • 理论上可以,但必须重新创建整个表达式树并代入值。 Expression.Invoke 只是创建一个InvocationExpression。对于这个确切的示例,您可以这样做来进行替换:expr2 = Expression.Lambda&lt;Func&lt;TEntity,bool&gt;&gt;((expr1.Body as MethodCallExpression).Update(Expression.Constant(values), expr1.Parameters[0]), expr1.Parameters[0]) 并删除定义 param 的行
    【解决方案2】:

    我发布我的答案,以便其他人可以从中受益。

    完成我想做的最好的方法是使用ExpressionVisitorExpression.Lambda 并复制整个表达式并进行适当的更改。这个类是3/3.5的内部类,所以如果要在3/3.5使用这个类,here就是这个类的完整实现。网上有很多相同的教程。

    其次,任何想要获得解析和可视化表达式树的工具的人都应该获得 VS 2008 中的 ExpressionTreeVisualizer 示例。您必须将示例转换并编译为 VS 2010 以用于 .Net 4.0。 This blog post 由 Patrick Smacchia 为您提供帮助。

    对上述所有内容的附带说明,即使按照我在此答案中解释的方式转换查询后,nhibernate 仍会引发异常。仅当我尝试将查询转换为将来的查询然后对其进行评估时,才会发生这种情况。无需将查询转换为未来,它就可以正常工作。我正在寻找相同的解决方案。如果我发现它发生的原因以及解决方案是什么,我会相应地更新答案。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-29
      • 1970-01-01
      • 2013-03-13
      • 2015-03-29
      • 1970-01-01
      相关资源
      最近更新 更多