将GroupJoin语句转换为表达式树

时间:2016-06-16 17:58:13

标签: c# reflection expression-trees system.reflection

我尝试使用以下语句对两组数据进行外连接:

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     {         public int JoinField;         public decimal DataField;     }

OuterModel:     公共类OuterModel     {         public int JoinField;         public decimal DataField;     }

DestinationModel:     公共类DestinationModel     {         public int Id;         public decimal AggregationField;     }

inners是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();

我收到编译错误:

方法的类型参数&#39; Enumerable.GroupJoin(IEnumerable,IEnumerable,Func,Func,Func,TResult&gt;)&#39;无法从使用中推断出来。尝试明确指定类型参数。

我知道我只是遗漏了一些明显的东西,但经过几个小时的努力,我没有看到它。有人能指出我正确的方向吗?

1 个答案:

答案 0 :(得分:0)

我找到了答案。我需要放置DefaultIfEmpty调用并为其提供Select调用的结果。我为DefaultIfEmpty调用创建了一个MethodInfo:

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

然后我创建了一个lambda表达式,它调用DefaultIfEmpty并一起选择:

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