实体框架 - 如何检查对象是否在另一个对象的层次结构下?

时间:2013-08-22 04:05:22

标签: asp.net-mvc entity-framework

我有一个名为Contents的表。内容上有一对多的关系,因此每个内容都可以有父母和孩子。我使用的是EF Code First,因此我的实体Content具有IdParentIdParentChildren属性。

现在,我正在建立一个基于ajax的内容树。我有一个简单的操作,它根据parentId返回一个内容级别的JSON:

public JsonResult GetContents(int? parentId = null)
{
    return Json(db.Contents
        .Where(p => p.ParentId == parentId)
        .Select(p => new
        {
            id = p.Id,
            name = p.Name
        });
}

我要做的下一件事是自动选择一些值。问题是该值可以在树的层次结构内部,因此对于每个内容,我需要知道所选择的值是否是子或孙等等。

public JsonResult GetContents(int? parentId = null, int selectedValue)
{
    return Json(db.Contents
        .Where(p => p.ParentId == parentId)
        .Select(p => new
        {
            id = p.Id,
            name = p.Name
            isSelectedValueUnderThisHierarchy: // How can I efficiently implement this? 
        });
}

通过大量查询很容易实现,但我尝试尽可能提高效率,据我所知,EF并没有提供任何递归方法,所以我真的不知道从哪里开始。

1 个答案:

答案 0 :(得分:1)

您可以先从所选值构建所有ParentId的列表。根据Contents表的大小,您可以先加载数据,然后循环浏览而不对数据库进行额外查询。

db.Contents.Load();
var selectedItem = db.Contents.Find(selectedValue);

var parents = new List<int>();
while (selectedItem.ParentId != null)
{
    parents.Add(selectedItem.ParentId.Value);
    selectedItem = selectedItem.Parent;
}    

或者,您可以使用CTE(公用表表达式)。

var parents = db.Database.SqlQuery<int>("sql statement");

获得父母列表后,您可以使用Contains

return Json(db.Contents
    .Where(p => p.ParentId == parentId)
    .Select(p => new
    {
        id = p.Id,
        name = p.Name
        isSelectedValueUnderThisHierarchy = p.ParentId.HasValue && parents.Contains(p.ParentId.Value)
    });

更新:CTE示例

您可能希望使用存储过程,但此代码应该可以使用。

var sql = @"with CTE as
    (
        select ParentId
        from Contents
        where Id = {0}
        union all
        select Contents.ParentId
        from Contents
            inner join CTE on Contents.Id = CTE.ParentId    
    )

    select *
    from CTE
    where ParentId is not null";
var parents = db.Database.SqlQuery<int>(string.Format(sql, selectedItem)).ToList();