2016-06-16 4 views
0

je tente de faire une jointure externe sur deux ensembles de données à l'aide de cette déclaration:une déclaration GroupJoin à une expression arbre

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

Cela fonctionne correctement sans problème, mais je finalement besoin de convertir en un arbre d'expression pour permettre aux ensembles de données et aux champs de changer.

Mes modèles de données ressemblent à ceci:

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

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

DestinationModel: public class DestinationModel { Id public int; public decimal AgrégationField; }

est un inners List<InnerModel>

Outers est un List<OuterModel>

J'ai réussi à obtenir la plupart du temps, mais je tombe à la dernière courte étape. C'est ce que j'ai jusqu'à présent:

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

Tout fonctionne jusqu'à ce point. Lorsque je tente de brancher le innerKeySelector dans la déclaration originale:

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

je reçois une erreur de compilation:

Les arguments de type pour la méthode « Enumerable.GroupJoin (IEnumerable, IEnumerable, Func, Func, Func, TResult >) 'ne peut être déduit de l'utilisation. Essayez de spécifier explicitement les arguments de type. Je sais que je manque quelque chose d'évident, mais après avoir travaillé dessus pendant des heures, je ne le vois pas. Quelqu'un peut me diriger dans la bonne direction?

+0

Sauf que les classes de modèle devaient être renommées en 'xxxTestModel', je n'obtiens aucune erreur de compilation avec le code affiché. –

+0

J'ai corrigé les noms des classes dans le code, et simplifié certains des appels, mais j'ai toujours l'erreur. – MichaelDotKnox

Répondre

0

J'ai trouvé ma réponse. Je devais placer l'appel DefaultIfEmpty et lui donner le résultat de l'appel Select. J'ai créé un MethodInfo pour l'appel DefaultIfEmpty:

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

Puis je crée une expression lambda qui appelle DefaultIfEmpty et SELECT:

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

Cela m'a permis d'appeler les méthodes finales:

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