简体   繁体   English

将GroupJoin语句转换为表达式树

[英]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 DestinationModel { Id = inner.JoinField, AggregationField = outer.DataField })
            .DefaultIfEmpty(new DestinationModel { 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; InnerModel:公共类InnerModel {public int JoinField; public decimal DataField; 公共十进制DataField; } }

OuterModel: public class OuterModel { public int JoinField; OuterModel:公共类OuterModel {public int JoinField; public decimal DataField; 公共十进制DataField; } }

DestinationModel: public class DestinationModel { public int Id; DestinationModel:公共类DestinationModel {public int Id; public decimal AggregationField; 公共十进制AggregationField; } }

inners is a List<InnerModel> inners是一个List<InnerModel>

outers is a List<OuterModel> List<OuterModel>是一个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 (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);

Everything works up until this point. 到现在为止一切正常。 When I try to plug the innerKeySelector into the original statement: 当我尝试将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();

I get a compile error: 我收到一个编译错误:

The type arguments for method 'Enumerable.GroupJoin(IEnumerable, IEnumerable, Func, Func, Func, TResult>)' cannot be inferred from the usage. 无法从用法中推断方法“ Enumerable.GroupJoin(IEnumerable,IEnumerable,Func,Func,Func,TResult>)”的类型参数。 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? 有人可以指出我正确的方向吗?

I found my answer. 我找到了答案。 I needed to place the DefaultIfEmpty call and give it the result of the Select call. 我需要放置DefaultIfEmpty调用并为其提供Select调用的结果。 I created a MethodInfo for the DefaultIfEmpty call: 我为DefaultIfEmpty调用创建了MethodInfo:

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

Then I created a lambda expression that calls DefaultIfEmpty and Select together: 然后,我创建了一个lambda表达式,该表达式同时调用DefaultIfEmpty和Select:

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

That enabled me to call the final methods: 这使我能够调用最终方法:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM