如何在LINQ to Entities中展平层次结构?

时间:2012-05-20 08:09:28

标签: sql sql-server linq entity-framework linq-to-entities

我有一个类Org,它有ParentId(指向消费者)和Orgs属性,以启用Org实例的层次结构。我还有一个Customer类,它有OrgId属性。给定名为Owner的任何Org实例,如何检索该组织的所有Customer实例?也就是说,在LINQ之前,我会以Owner为根进行“手动”遍历组织树。我确信存在一些更简单的东西。

示例:如果我的根级别组织名为“电影”,ID为“1”,子组织称为“恐怖”,其中ParentId为“1”,Id为23,我想查询所有客户在电影下,所以我必须为所有客户提供1和23的OrgId。

2 个答案:

答案 0 :(得分:1)

Linq对此没有帮助,但SQL Server会。

创建一个CTE以生成一个扁平的Org Ids列表,如:

CREATE PROCEDURE [dbo].[OrganizationIds]
    @rootId int

AS
    WITH OrgCte AS 
    ( 
        SELECT OrganizationId FROM Organizations where OrganizationId = @rootId
        UNION ALL 
        SELECT parent.OrganizationId FROM Organizations parent
        INNER JOIN OrgCte child ON parent.Parent_OrganizationId = Child.OrganizationId
    ) 
    SELECT * FROM OrgCte 

RETURN 0

现在向映射到此存储过程的上下文添加一个函数import。这会在您的上下文中生成一个方法(返回的值可以为n,因为原始的Parent_OrganizationId被声明为INT NULL):

public partial class TestEntities : ObjectContext
{
    public ObjectResult<int?> OrganizationIds(int? rootId)
    {
        ...

现在您可以使用如下查询:

// get all org ids for specific root.  This needs to be a separate 
// query or LtoE throws an exception regarding nullable int.
var ids = OrganizationIds(2);

// now find all customers
Customers.Where (c => ids.Contains(c.Organization.OrganizationId)).Dump();

答案 1 :(得分:0)

不幸的是,在Entity Framework中并非原生。您需要构建自己的解决方案。可能你需要迭代到root。您可以通过要求EF一次性获得一定数量的父母来优化此算法:

...
select new { x.Customer, x.Parent.Customer, x.Parent.Parent.Customer }

使用此方法限制为静态固定数量的父级(此处:3),但它将为您节省2/3的数据库往返次数。

编辑:我认为我的数据模型没有正确,但我希望这个想法很明确。

编辑2:为了回应您的评论和编辑,我改编了这样的方法:

var rootOrg = ...;
var orgLevels = new [] {
 select o from db.Orgs where o == rootOrg, //level 0
 select o from db.Orgs where o.ParentOrg == rootOrg, //level 1
 select o from db.Orgs where o.ParentOrg.ParentOrg == rootOrg, //level 2
 select o from db.Orgs where o.ParentOrg.ParentOrg.ParentOrg == rootOrg, //level 3
};
var setOfAllOrgsInSubtree = orgLevels.Aggregate((a, b) => a.Union(b)); //query for all org levels

var customers = from c in db.Customers where setOfAllOrgsInSubtree.Contains(c.Org) select c;

请注意,这仅适用于有界最大树深度。在实践中,通常就是这种情况(如10或20)。

性能不会很好,但它只是LINQ到实体的解决方案。