【问题标题】:How to count the number of items that exist in a table efficiently with EntityFramework?如何使用 EntityFramework 有效地计算表中存在的项目数?
【发布时间】:2020-05-04 10:11:09
【问题描述】:

我有一个 C# 项目,其中几个项目存储在不同的表中,例如,要计算一个表包含多少元素,我会执行类似于以下操作的操作:

public int getLengthListProducts(int idCompany)
{
  try
  {
      using (var context = new ccoFinalEntities())
      {
        return context.products.Where(p => true == p.status && idCompany == p.idCompany).ToList().Count;
      }
  }
  catch
  {
    return -1;
  }
}

到目前为止,它工作得很好,但是当数量开始为 1000 件时,在某些 PC 上开始需要一段时间才能获得这个数字。

我怀疑 context.products 将所有项目放在 RAM 中,然后开始提取并计算满足以下条件的项目,这就是为什么应用程序在计数完成之前被冻结的原因。

我的问题是:有没有办法做得更好?

例如,我认为我应该直接使用 SQL 语句而不是使用 EntityFramework 来获取该数字,但我不知道这是否是一个好主意,或者 EntityFramework 是否有更有效的方法。

欢迎任何cmets或建议。

【问题讨论】:

  • 猜测一下,删除ToList()
  • 对于初学者来说,不需要.Where().ToList().Count。只需使用您的谓词直接调用.Count()
  • 如果你确实需要 ToList() 出于任何其他原因,最好是你做的最后一件事
  • 建议:你的catch有点烂,你为什么要回-1?您的表可能不包含 -1 行。这样所有调用者都需要检查结果-1?如果您无法在此处“修复”异常,则不应在此级别捕获它。

标签: c# sql entity-framework count tolist


【解决方案1】:
context.products.Where(p => true == p.status && idCompany == p.idCompany).ToList().Count;

在这个 Linq 查询中,ToList() 将生成 SQL 查询:

SELECT ...
FROM Products
WHERE status = 1 and idCompagny = @idCompany

此查询在您的数据库上执行,并且可以返回很多行。 所有元素都加载到 .Net Collection 中的客户端内存中,Count 返回最终结果。

借助 Entity Framework,您可以使用聚合 Linq 查询(Count、Sum、Avg、...):

https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/sql/linq/aggregate-queries

例子:

context.products.Where(p => true == p.status && idCompany == p.idCompany).Count();

Count() 将生成 SQL 查询:

SELECT COUNT(*)
FROM Products
WHERE status = 1 and idCompagny = @idCompany

查询在您的数据库上执行并返回标量结果。

【讨论】:

  • 可以简写为:context.products.Count(p => true == p.status && idCompany == p.idCompany);
  • 最好在Where中设置where条件,这只是为了性能...
【解决方案2】:
context.products.Count(p => p.status == true && idCompany == p.idCompany);

context.products.Where(p => p.idCompany == idCompany)
                .Count(p => p.status == true);

(为了可读性)

足够了。

【讨论】:

    【解决方案3】:

    对于 SQL-Server,您可以这样做

       string cmd = @"SELECT
            t.NAME AS TableName,
            s.Name AS SchemaName,
            p.rows AS RowCounts,
            SUM(a.total_pages) * 8 AS TotalSpaceKB,
            SUM(a.used_pages) * 8 AS UsedSpaceKB,
            (SUM(a.total_pages) - SUM(a.used_pages)) * 8 AS UnusedSpaceKB
        FROM
            sys.tables t
        INNER JOIN
            sys.indexes i ON t.OBJECT_ID = i.object_id
        INNER JOIN
            sys.partitions p ON i.object_id = p.OBJECT_ID AND i.index_id = p.index_id
        INNER JOIN
            sys.allocation_units a ON p.partition_id = a.container_id
        LEFT OUTER JOIN
            sys.schemas s ON t.schema_id = s.schema_id
        WHERE
            t.NAME NOT LIKE 'dt%'
            AND t.is_ms_shipped = 0
            AND i.OBJECT_ID > 255
        GROUP BY
            t.Name, s.Name, p.Rows
        "
        db.Database.SqlQuery(cmd).ToList<Statistic>();
    

        public class Statistic
        {
            public string TableName { get; set; }
            public string SchemaName { get; set; }
            public long RowCounts { get; set; }
            public long TotalSpaceKB { get; set; }
            public long UsedSpaceKB { get; set; }
            public long UnusedSpaceKB { get; set; }
        }
    

    然后您可以快速了解所有表,包括内存大小。

    我知道硬编码 SQL 不是首选,但由于它仅与系统表有关,因此您可以假设它们永远不会改变,这是可以接受的。

    【讨论】:

    • 这对于计算与特定谓词匹配的行数没有用,正如原始问题中使用的那样。它对维护脚本更有用,但即使有,也应注意sys.partitions.rows 或多或少是“尽力而为”,有时可能无法反映实际的行数(例如,参见this question)。跨度>
    • 也许,但问题包含短语计算一个表包含多少元素,所以也许这很有趣。实际上这是在使用 system.partition.rows,只是带有额外的信息,比如表名,它不在这个表中。
    猜你喜欢
    • 2011-10-07
    • 2019-02-11
    • 2019-12-04
    • 1970-01-01
    • 1970-01-01
    • 2020-05-04
    • 2011-05-25
    • 2023-03-29
    • 1970-01-01
    相关资源
    最近更新 更多