2016-12-15 40 views
3

我試圖用Linq/LinqKit構建一個嵌套的查詢。理論上這似乎很容易。但我堅持實用的部分。使用Linq(套件)嵌套數據的StackOverflowException

在我的數據庫中,我有一個表格,它有一個自引用其父。在我的linq-query中,我現在想要選擇給定元素的所有父母(以及這個的父母等等)。

在我的代碼有部分類的MyTable下面的表達式:

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

ParentId設置應該選擇一個給定的實體的父母和那些家長。

查詢本身(簡化):

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

運行該代碼在StackOverflowException最終作爲停止條件沒有命中,因此Parents -call被無限嵌套直到堆棧已滿。

任何想法如何做到這一點或者這是不可能的?或者還有其他方法在一個查詢中使用Linq/LinqKit獲取嵌套數據嗎?

我已經嘗試過通過上下文來表達,以創建一個子查詢(也未工作):

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

嗨,有趣的嘗試!但是,不可以創建具有未知最大嵌套級別的可擴展(EF兼容)遞歸表達式,因爲沒有等效的SQL CTE。 –

+0

@IvanStoev,如果我知道嵌套的最高級別怎麼辦?已經嘗試傳遞每次遞歸調用遞減的深度參數。但是這裏同樣的例外 – KingKerosin

回答

0

正如在評論中提到,目前這是不可能創建一個遞歸擴展的(即不可調用)表達式。

但是,如果可以限制最大深度,一種可能的解決方案將是建立這樣的表達式(利用EF導航屬性):

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

動態:

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

和使用它如下:

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