2012-06-26 2 views
0

EDIT :보다 자세한 질문.하나의 표현 트리를 다른 것으로 변환/삽입

SQL Server에서 2100 매개 변수 제한 크기를 극복하기 위해 특별히 In 쿼리에 대해 nHibernate에서 일괄 처리 작업을하고 있습니다. 이를 위해

, 나는 (이것은 매우 단순화 된 버전입니다)이 생성자를 가진 클래스를 만들었습니다

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() 

이 사람이 올바른 방향으로 날 포인트 :

다음 는 스택 추적입니까? 어떤 도움을 주시면 감사하겠습니다.

감사합니다.
Yogesh.

답변

0

다른 사람들이 혜택을 누릴 수 있도록 내 답변을 게시하고 있습니다.

내가 원하는 것을 수행하는 가장 좋은 방법은 ExpressionVisitorExpression.Lambda을 사용하고 적절한 변경 내용으로 전체 표현식의 복사본을 만드는 것입니다. 이 클래스는 3/3.5의 내부 클래스이므로,이 클래스를 3/3.5로 사용하려면 here이 클래스의 전체 구현입니다. 그물에 동일을 위해 유효한 과료의 톤이있다.

둘째, 표현 트리를 구문 분석하고 시각화하기위한 도구를 얻으려는 사람은 VS 2008에서 ExpressionTreeVisualizer 샘플을 얻어야합니다. .Net 4.0 용으로 VS 2010으로 샘플을 변환하고 컴파일해야합니다. Patrick Smacchia의 This blog post이 어떻게 도움이되는지 알려드립니다.

위의 모든 것에 대한 한 쪽 메모, nhibernate는이 대답에서 설명한 방식으로 쿼리를 변환 한 후에도 여전히 예외를 throw합니다. 이 쿼리는 향후 쿼리로 변환 한 다음 평가하려고하는 경우에만 발생합니다. 미래에 쿼리를 변환하지 않고도 제대로 작동합니다. 나는 같은 것을위한 해결책을 찾고있다. 나는 왜 그것이 일어나고 있는지 그리고 해결책이 무엇인지를 발견하면 그에 따라 대답을 업데이트 할 것입니다.

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

감사합니다. 나 좀 가자. 어떤 방법이 있어도, 원래의 expr'에서'l'을 대체 할 수 있습니까? 나는'Invoke'를 호출하지 않고 expr2를 만드는 것을 의미합니까? – Yogesh

+0

이론에서는 가능하지만 전체 표현식 트리를 다시 작성하고 값을 대체해야합니다. 'Expression.Invoke'는 단지'InvocationExpression'을 생성합니다. 이 정확한 예제에서는 대체를하기 위해 이렇게 할 수 있습니다 : 'expr2 = Expression.Lambda > ((expr1.Body as MethodCallExpression) .Update (Expression.Constant (values), expr1.Parameters [ 0]), expr1.Parameters [0])' 그리고'param'을 정의하는 라인을 삭제하십시오 – JKor

관련 문제