2016-06-16 16 views
0

ich versucht bin eine äußere auf zwei Sätze von Daten beitreten zu tun mit dieser Aussage:Konvertieren eine GroupJoin-Anweisung in einen Ausdruck Baum

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

Dies funktioniert korrekt ohne Problem, aber ich muß schließlich diese konvertieren ein Ausdrucksbaum, um den Datasets und den Feldern zu erlauben, sich zu ändern.

Meine Datenmodelle wie folgt aussehen:

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 ist ein List<InnerModel>

outers ein List<OuterModel>

ist, ich habe es geschafft, die meisten der Weg zu bekommen, aber ich bin kurz im letzten Schritt fallen. Das habe ich bisher:

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

Alles funktioniert bis zu diesem Punkt. Wenn ich versuche, die innerKeySelector in die ursprüngliche Aussage zu stopfen:

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

ich einen Compiler-Fehler erhalten:

Die Art Argumente für die Methode ‚Enumerable.GroupJoin (IEnumerable, IEnumerable, Func, Func, Func, TResult >) 'lässt sich aus der Verwendung nicht ableiten. Versuchen Sie, die Typargumente explizit anzugeben.

Ich weiß, ich vermisse nur etwas Offensichtliches, aber nachdem ich stundenlang daran gearbeitet habe, sehe ich es nicht. Kann mir jemand in die richtige Richtung zeigen?

+0

Abgesehen davon, dass Modellklassen in 'xxxTestModel' umbenannt werden mussten, erhalte ich mit dem geposteten Code keinen Compilerfehler. –

+0

Ich habe die Namen der Klassen im Code korrigiert und einige der Aufrufe vereinfacht, aber ich bekomme immer noch den Fehler. – MichaelDotKnox

Antwort

0

Ich fand meine Antwort. Ich musste den DefaultIfEmpty-Aufruf platzieren und ihm das Ergebnis des Select-Aufrufs geben. Ich habe eine Methodinfo für den DefaultIfEmpty Aufruf:

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

Dann habe ich einen Lambda-Ausdruck, der DefaultIfEmpty Anrufe und wählen zusammen:

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

Das ermöglichte mir die letzten Methoden aufrufen:

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