【问题标题】:EF recursive hierarchyEF 递归层次结构
【发布时间】:2013-11-22 05:17:57
【问题描述】:

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

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

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

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

【问题讨论】:

  • 首先将IEnumerable 替换为IQueryable,这将允许EF 至少尝试从中创建一个大查询。 IEnumerable 将强制 EF 在继续之前先填充列表。 Join 也可以工作,但会使数据集变得庞大,因为它会复制第一个项目的每个子项目的所有数据,甚至更远。
  • 好吧。 IQueryable 不起作用,因为接口 IHierarchyEntity => Childs 是自定义属性。它抛出一个“NotSupportedException”。如果我只是选择我需要的列,加入将是可能的。 @jessehouwing

标签: c# sql linq entity-framework recursion


【解决方案1】:

由于这可能是您应用程序核心中相当固定的层次结构,因此我不介意为它编写一段专用代码。此外,使用 LINQ to a database backend 为分层查询编写有效的通用例程是不可能的。 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();
}

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

【讨论】:

  • 谢谢。我真的希望我可以避免使用 db 程序。
【解决方案2】:

据我所知,您试图提取您的行为的非常长的历史。我需要创建一个例程,以便在发生更改时更新您的统计信息。这没有您应该弄清楚的“终极”解决方案。例如。我有“进”和“出”库存交易,并找出我应该经历 20 年历史的所有项目的当前库存水平。为了解决这个问题,我可以做每月总结,只计算从月初开始的变化。或者我可以使用数据库触发器在更改发生后立即更新我的摘要(可能是性能成本高的一个)。或者我可以有一个会不时更新它的服务(可能不会 100% 更新)。换句话说,您需要表格/类,这将使您的汇总结果随时可用。

【讨论】:

  • 所以每次发生变化时你都会更新计算?
猜你喜欢
  • 2017-02-04
  • 1970-01-01
  • 2021-07-04
  • 1970-01-01
  • 1970-01-01
  • 2023-03-24
  • 2015-11-23
  • 2016-04-15
  • 1970-01-01
相关资源
最近更新 更多