EF递归层次结构

时间:2013-11-10 10:51:06

标签: c# sql linq entity-framework recursion

我的应用程序中有一些图层。这些层遵循以下结构:公司有定居点,定居点有部分,部分有机器,机器生产物品,生产机器需要工具的物品,...... 在该层次结构的最后,有条目可以使用工具的特定部分(称为cuttingtool)生成多少项。基于此,可以计算统计量。在每一层上,添加下一个上层的统计结果。 看看这个图: enter image description here

在每个图层上,显示统计信息。例如:用户导航到第二层(Items)。共有10个条目。用户可以看到一个饼图,显示每个项目的成本。这些成本是通过添加项目工具(下一个上层)的所有成本来计算的。这些工具的成本是通过增加“工具部件”的所有成本来计算的...... 我知道这有点复杂所以如果有任何问题,请问我更详细的解释。

现在我的问题:要计算项目的成本(为机器,工具提供相同的统计数据,... =>对于图上的每一层),我需要获得项目的所有生命周期。所以我使用递归调用来跳过Item和Lifetime之间的所有层。 这工作得很好,但我使用很多SelectMany-linq命令。结果,性能非常糟糕。 我已经考虑过连接或程序(存储在数据库中)以加快速度,但到目前为止我还没有遇到像数据库这样的技术。所以我想问你,你会做什么? 目前我正在使用类似的东西:

public IEnumerable<IHierachyEntity> GetLifetimes(IEnumerable<IHierachyEntity> entities)
{
    if(entities is IEnumerable<Lifetime>)
    {
        return entities;
    }
    else
    {
        return GetLifetimes(entities.SelectMany(x => x.Childs))
    }
}

2 个答案:

答案 0 :(得分:1)

尽管我理解你试图拉长你行动的历史。我需要创建一个例程,它将在发生更改时更新您的统计信息。这没有你应该弄明白的“终极”解决方案。例如。我有“进入”和“出去”股票交易,并找出我应该经历20年历史的所有项目的当前库存水平。为了解决这个问题,我可以做月度摘要,只计算从月份开始的变化。或者,一旦发生更改,我就可以使用数据库触发器来更新我的摘要(可能性能成本很高)。或者我可以提供不时更新的服务(可能不会100%更新)。换句话说,您需要使用表/类来保持聚合结果的可用性。

答案 1 :(得分:1)

由于这可能是您应用程序核心中非常固定的层次结构,因此我不介意为其编写专用代码。此外,LINQ到数据库后端不可能为分层查询编写有效的通用例程。 n+1问题无法避免。

所以做这样的事情:

public IQueryable<Lifetime> GetLifetimes<T>(IQueryable<T> entities)
{
    var machines = entities as IQueryable<Machine>;
    if (machines != null)
        return machines.SelectMany (m => m.Items)
                       .SelectMany (i => i.Tools)
                       .SelectMany (i => i.Parts)
                       .SelectMany (i => i.Lifetimes);

    var items = entities as IQueryable<Item>;
    if (items != null)
        return items.SelectMany (i => i.Tools)
                    .SelectMany (i => i.Parts)
                    .SelectMany (i => i.Lifetimes);

    var tools = entities as IQueryable<Tool>;
    if (tools != null)
        return tools.SelectMany (i => i.Parts)
                    .SelectMany (i => i.Lifetimes);

    var parts = entities as IQueryable<Part>;
    if (parts != null)
        return parts.SelectMany (i => i.Lifetimes);

    return Enumerable.Empty<Lifetime>().AsQueryable();
}

重复代码,是的,但它很清楚会发生什么,它可能是代码中最稳定的部分。当需要持续维护时,重复代码是一个潜在的问题。