如何快速查找已加载实体列表中的项目

时间:2015-01-16 15:25:29

标签: performance entity-framework c#-4.0 collections

我已经构建了一个MVC 5应用程序,使用EF 6来查询数据库。一页显示两个维度的交叉表:针对这些物质的特性的物质。它呈现为一个html表。许多单元格没有值。这就是它的样子:

        sub 1  sub 2  sub 3
prop A   1.0
prop B          1.5     X
prop C   0.6            Y

单元格值实际上更复杂,包括工具提示,脚注等。

我通过以下步骤实现了html表的生成:

  1. 创建唯一属性列表;
  2. 创建一系列独特物质;
  3. 遍历属性;
  4. 为每个呈现一行;
  5. 循环物质;
  6. 查看属性和物质组合是否有值;
  7. 渲染单元格的值或空单值。
  8. 使用ANTS性能分析器,我发现步骤6存在巨大的性能问题,物质和性质数量不断增加,命中数量猛增至数亿,数百种物质和数十种物质(用户可以做出的最大选择)。执行时间是几分钟。它似乎比例N(物质)^ 2 * N(属性)^ 2。

    代码如下:

    Value currentValue = 
    values.Where(val => val.substance.Id == currentSubstanceId 
    && val.property.Id == currentPropertyId).SingleOrDefault();
    

    其中values是List,Value是一个实体,我从中读取以呈现单元格。值已从数据库预加载,SQL Server Profiler不显示任何查询。

    由于并非所有单元格都有值,我认为最好循环遍历行和列,看看是否有值。我不能只循环遍历值列表。

    我可以尝试改善这一点吗?我想过:

    1. 创建某种C#对象,使用substance.Id和property.Id作为复合键并从List对象中填充它。哪个会最快?
    2. 创建一些Linq查询,该查询返回一个已包含空单元格的对象,如(物质交叉连接属性)左连接值。我可以轻松地在SQL中执行此操作,但这可以通过Linq完成吗?存储结果的对象是否可以将Value作为成员字段,因此我仍然可以使用它来呈现单元格?
    3. 停止预加载,只需对每个组合的值运行数据库查询,可能会从数据库索引中受益。
    4. 我正在考虑限制用户可以选择的物质和属性的数量,但我宁愿不这样做。
    5. 添加信息

      根据C.Zonnenberg的要求,有关该查询的更多信息。

      填充值列表的查询基本如下: 我创建了一个IQueryable,我为其添加了所请求物质和属性的过滤器。然后,我将相关实体中的物质,财产和价值详情包括在内。然后我执行query.ToList()。 SQL Profiler看到的实际SQL查询看起来很复杂,涉及SubstanceId IN()和PropertyId IN(),但它执行的时间远远少于一秒。

      它返回一个代理列表,如:{System.Data.Entity.DynamicProxies.SubstancePropertyValue_078F758A4FF9831024D2690C4B546F07240FAC82A1E9D95D3826A834DCD91D1E}

1 个答案:

答案 0 :(得分:1)

我认为你最好的选择是你的第一选择。但要有效地做到这一点,我还会修改源数据(values)并将其转换为字典,因此您有一个针对索引查找进行了优化的结构:

var dict = values.ToDictionary(e => 
                       Tuple.Create(e.substance.id, e.propertyid),
                       e => e.Value);

然后对每个细胞:

Value currentValue ;
dict.TryGetValue(Tuple.Create(currentSubstanceId, currentPropertyId), 
                 out currentValue );

此外,例如,通过在Parallel.ForEach循环遍历所有物质中获取单元格值,您可以从并行化中受益。