2012-06-26 34 views
0

編輯:更詳細的問題。轉換/嵌入一個表達式樹到另一個

我正在研究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() 

可有人點我的方向是正確的?任何幫助將不勝感激。

Regards,
Yogesh。

回答

0

我發佈我的答案,以便其他人可以從中受益。

做我想做的事情的最好方法是使用ExpressionVisitorExpression.Lambda,並用適當的改變複製整個表達式。這個類是3/3.5的內部類,所以如果你想使用這個類的3/3.5,here是類的完整實現。網上有很多相同的教程。其次,任何想要獲得表達式樹解析和可視化工具的人都應該在VS 2008中獲得ExpressionTreeVisualizer示例。您必須將示例轉換並編譯到VS 2010 for .Net 4.0。 Patrick Smacchia的This blog post可以幫助你。

對上述所有內容的一面說明,即使按照我在此答案中解釋的方式轉換查詢後,nhibernate仍會拋出異常。這隻會發生,如果我試圖將查詢轉換爲未來的查詢,然後評估它。沒有將查詢轉換爲未來,它工作正常。我正在尋找相同的解決方案。如果我發現問題的原因以及解決方案,我會相應地更新答案。

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); 
+0

謝謝。讓我走一點。有什麼方法,我可以在原始表達式中替換'l'嗎?我的意思是make expr2而不調用'Invoke'? – Yogesh

+0

理論上你可以,但你必須重新創建整個表達式樹並替換值。 'Expression.Invoke'只是創建一個'InvocationExpression'。對於這個確切的例子,你可以這樣做替換: 'expr2 = Expression.Lambda >((作爲MethodCallExpression的Expr1.Body).Update(Expression.Constant(values),expr1.Parameters [ 0]),expr1.Parameters [0])' 並刪除定義'param'的行 – JKor