2016-06-16 6 views
0

Я пытающегося сделать внешнее соединение по двум наборам данных, используя следующее выражение:Преобразование заявление GroupJoin к Expression Tree

var destinationList = inners.GroupJoin(outers, inner => inner.JoinField, outer => outer.JoinField, 
    (inner, outerList) => 
     outerList.Select(outer => new DestinationModel { Id = inner.JoinField, AggregationField = outer.DataField }) 
      .DefaultIfEmpty(new DestinationModel { Id = inner.JoinField })).SelectMany(destination => destination).ToList(); 

Это работает правильно без проблем, но я в конечном счете нужно преобразовать это дерева выражений, позволяющего изменять наборы данных и поля.

Мои модели данных выглядит следующим образом:

InnerModel: общественный класс InnerModel { общественного ИНТ JoinField; public decimal DataField; }

OuterModel: общественный класс OuterModel { общественного ИНТ JoinField; public decimal DataField; }

DestinationModel: общественный класс DestinationModel { общественного ИНТ Id; public decimal AggregationField; }

Внутренние составляющие является List<InnerModel>

Outers является List<OuterModel>

мне удалось получить большую часть пути, но я падаю короткий на последнем шаге. Это то, что у меня есть до сих пор:

// Declare variables 
var innerParameter = Expression.Parameter(typeof (InnerModel), "inner"); 
var innerSelect = Expression.Lambda<Func<InnerModel, int>>(Expression.Field(innerParameter, "JoinField"), innerParameter); 
var outerParameter = Expression.Parameter(typeof (OuterModel), "outer"); 
var outerListParameter = Expression.Parameter(typeof (IEnumerable<OuterModel>), "outerList"); 
var outerSelect = Expression.Lambda<Func<OuterModel, int>>(Expression.Field(outerParameter, "JoinField"), outerParameter); 
var existingBinding = Expression.MemberInit(Expression.New(typeof (DestinationModel)), Expression.Bind(typeof (DestinationModel).GetField("Id"), Expression.Field(innerParameter, "JoinField"))); 

// Create lambdas 
var selector = Expression.Lambda<Func<OuterModel, DestinationModel>>(existingBinding, outerParameter); 
var selectMethod = typeof (Enumerable).GetMethods().First(x => x.Name == "Select" && x.GetParameters().Length == 2).MakeGenericMethod(typeof(OuterModel), typeof(DestinationModel)); 
var selectCall = Expression.Call(selectMethod, outerListParameter, selector); 

// Create the inner key selector for the GroupJoin method 
var innerKeySelector = Expression.Lambda(selectCall, innerParameter, outerListParameter); 

Все работает до этого момента. Когда я пытаюсь подключить innerKeySelector в первоначальное заявление:

var result = inners.GroupJoin(outers, innerSelect.Compile(), outerSelect.Compile(), (inner, outerList) => outerList.Select(outer => new DestinationModel {Id = inner.JoinField, AggregationField = outer.DataField}).DefaultIfEmpty(new DestinationModel {Id = inner.JoinField})).SelectMany(destination => destination).ToList(); 

я получаю ошибку компиляции:

аргументы типа для метода «Enumerable.GroupJoin (IEnumerable, IEnumerable, Func, Func, Func, TResult >) 'не может быть выведено из использования. Попробуйте явно указать аргументы типа.

Я знаю, что мне просто не хватает чего-то очевидного, но после работы над этим в течение нескольких часов я не вижу его. Может ли кто-нибудь указать мне в правильном направлении?

+0

За исключением того, что пришлось переименовывать классы моделей в 'xxxTestModel', я не получаю никакой компиляции с опубликованным кодом. –

+0

Я исправил имена классов в коде и упростил некоторые вызовы, но до сих пор получаю ошибку. – MichaelDotKnox

ответ

0

Я нашел свой ответ. Мне нужно было поместить вызов DefaultIfEmpty и дать ему результат вызова Select. Я создал MethodInfo для DefaultIfEmpty вызова:

var defaultIfEmptyMethod = typeof (Enumerable).GetMethods().First(x => x.Name == "DefaultIfEmpty" && x.GetParameters().Length == 2).MakeGenericMethod(typeof (DestinationTestModel)); 

Затем я создал лямбда-выражение, которое вызывает DefaultIfEmpty и Select вместе:

var innerKeySelectorWithDefault = Expression.Lambda<Func<InnerTestModel,IEnumerable<OuterTestModel>,IEnumerable<DestinationTestModel>>>(Expression.Call(defaultIfEmptyMethod, selectCall, nonExistingBinding), innerParameter, outerListParameter); 

Это позволило мне назвать окончательные методы:

var result = inners.GroupJoin(outers, innerSelect.Compile(), outerSelect.Compile(),innerKeySelectorWithDefault.Compile()).SelectMany(destination => destination).ToList();