jeudi 16 juin 2016

Converting a GroupJoin statement to an Expression Tree

I'm attempting to do an outer join on two sets of data using this statement:

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

This works correctly without problem, but I ultimately need to convert this to an expression tree to allow the datasets and the fields to change.

My data models look like this:

InnerModel: public class InnerModel { public int JoinField; public decimal DataField; }

OuterModel: public class OuterModel { public int JoinField; public decimal DataField; }

DestinationModel: public class DestinationModel { public int Id; public decimal AggregationField; }

inners is a List<InnerModel>

outers is a List<OuterModel>

I've managed to get most of the way, but I'm falling short at the last step. This is what I have so far:

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

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

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

Everything works up until this point. When I try to plug the innerKeySelector into the original statement:

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

I get a compile error:

The type arguments for method 'Enumerable.GroupJoin(IEnumerable, IEnumerable, Func, Func, Func, TResult>)' cannot be inferred from the usage. Try specifying the type arguments explicitly.

I know I'm just missing something obvious, but after working on this for hours, I'm not seeing it. Can someone point me in the right direction?





Aucun commentaire:

Enregistrer un commentaire