2016-12-15 38 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) 
    });