2016-12-15 1 views
3

Ich versuche, eine verschachtelte Abfrage mit Linq/LinqKit zu erstellen. In der Theorie scheint das einfach zu sein. Aber ich bin mit dem praktischen Teil festgefahren.StackOverflowException mit Linq (Kit) für verschachtelte Daten

In meiner Datenbank habe ich eine Tabelle, die eine Selbstreferenz auf ihre Eltern hat. In meiner linq-Abfrage möchte ich nun alle Eltern eines bestimmten Elements (und die Eltern dieses Elements usw.) auswählen.

In meinem Code habe ich den folgenden Ausdruck in Teilklasse MyTable:

public static Expression<Func<MyTable, IEnumerable<MyTable>>> Parents => (entity) => entity.ParentId != null ? new[]{entity.ParentEntity}.Union(Parents.Invoke(entity.ParentEntity) : new MyEntity[]{}; 

, die die Eltern einer bestimmten Einheit und jene Eltern wählen sollte, wenn die ParentId eingestellt ist.

Die Abfrage selbst (vereinfachte):

dbContext 
    .MyTable 
    .AsExpandable() 
    .Where(x => x.Id == myId) 
    .Select(x => new 
    { 
     Parents = MyTable.Parents.Invoke(x, dbContext) 
    }); 

diesen Code Lauf endet in einem StackOverflowException auf, als die Stop-Bedingung nicht getroffen wird und damit der Parents -Aufruf verschachtelt ist endlos, bis der Stapel voll ist.

Irgendwelche Ideen wie das gemacht werden kann oder ist das nicht möglich? Oder gibt es eine andere Möglichkeit, verschachtelte Daten mit Linq/LinqKit innerhalb einer Abfrage abzurufen?

ich schon versucht, den Kontext zu dem Ausdruck, um eine Unterabfrage (auch nicht funktioniert) zu erstellen vorbei: in den Kommentaren

public static Expression<Func<MyTable, MyContext, IEnumerable<MyTable>>> Parents => (entity, dbContext) => entity.ParentId != null ? new[]{entity.ParentEntity}.Union(Parents.Invoke(dbContext.MyTable.FirstOrDefault(x => x.Id == entity.ParentId), dbContext) : new MyEntity[]{}; 
+0

Hallo, interessanter Versuch! Aber nein, es ist nicht möglich, einen erweiterbaren (EF-kompatiblen) rekursiven Ausdruck mit unbekannter maximaler Verschachtelungsebene zu erstellen, da es kein Äquivalent zu SQL CTE gibt. –

+0

@IvanStoev, Was wäre, wenn ich die maximale Verschachtelungstiefe kennen würde? Bereits versucht, einen Tiefenparameter zu übergeben, der durch jeden rekursiven Aufruf dekrementiert wird. Aber die gleiche Ausnahme hier – KingKerosin

Antwort

0

Wie bereits erwähnt, derzeit nicht möglich, es ist eine rekursive erweiterbar zu erstellen (dh nicht aufrufbarer) Ausdruck.

Wenn Sie jedoch die maximale Tiefe begrenzen, wäre eine mögliche Lösung Ausdruck wie diese zu bauen (die EF Navigationseigenschaft Nutzung):

Parents = new MyTable [] { x.Parent, x.Parent.Parent, x.Parent.Parent.Parent, ...} 
    .Where(e => e != null) 

dynamisch:

static Expression<Func<MyTable, IEnumerable<MyTable>>> ParentsSelector(int maxLevels) 
{ 
    var parameter = Expression.Parameter(typeof(MyTable), "x"); 
    var parents = new Expression[maxLevels]; 
    for (int i = 0; i < parents.Length; i++) 
     parents[i] = Expression.Property(i > 0 ? parents[i - 1] : parameter, "Parent"); 
    Expression<Func<MyTable, bool>> predicate = x => x != null; 
    var result = Expression.Call(
     typeof(Enumerable), "Where", new[] { parameter.Type }, 
     Expression.NewArrayInit(parameter.Type, parents), predicate); 
    return Expression.Lambda<Func<MyTable, IEnumerable<MyTable>>>(result, parameter); 
} 

und Verwendung es wie folgt:

var parents = ParentsSelector(10); 
var query = dbContext.MyTable 
    .AsExpandable() 
    .Where(x => x.Id == myId) 
    .Select(x => new 
    { 
     Parents = parents.Invoke(x) 
    });