【问题标题】:C# Linq efficient way to count items on a List on the flyC# Linq 动态计算列表中项目的有效方法
【发布时间】:2015-07-02 19:25:32
【问题描述】:

我正在使用带有 EF 的 Linq to Entities 并希望有一种有效的方法来做到这一点。我正在做的是遍历一个列表,计算列表中的不同项目,将计数附加到一个元素上并使用 String.Join 返回一个字符串。我想要(并且我实现)是这样的

一(3),二(1),三(2)

来自具有此类项目的列表

一、三、一、三、二、一

如果我没有从我的 POCO 类中检索并为我的数据库中的每个条目即时处理所有这些并将列表传递给我的 DataGridView,这会更简单。

我的代码是这样的,

public class Module
{

     //Other fields here

    public string PartNumber { get; set; }

    [ForeignKey("PartNumber")]
    public Part Part { get; set; }

    [ForeignKey("Location")]
    public string WarehouseID { get; set; }

    public Warehouse Location { get; set; }
}

还有一个

public class Warehouse
{
       //Other fields here

    public List<Module> Modules { get; set; }

}

然后这里是我检索列表的 POCO 类,对于每个实体,我想出了一个绑定到我的 datagridview 的字符串。

public class Part{

      //Other fields

    public string Locations
    {
        get
        {
            //I don't know how efficient this is but I feel that it helps
            if (Modules.Count() < 1)
                return "";

            AirtelDataContext context = new AirtelDataContext();

            var modules = context.Modules.Include("Location")
                .Where(e => e.PartNumber == PartNumber && e.Location.WarehouseType != "Site")
                .Select(a => a.Location.WarehouseName)
                .ToList();

            var q = from x in modules
                    group x by x into g
                    let count = g.Count()
                    orderby count descending
                    select (g.Key + " (" + count + ")").ToString();

            return String.Join(", ", q);
        }
    }

}

正是这个只读的Location属性,我想提高它的效率。我的数据库 (MySql) 将包含少于 7000 个(可能最多 2000 个 Part 实体、2000 个 仓库 实体和最多 5000 个模块实体)

如果我能稍微提高性能,我将不胜感激。将部件实体加载到 DataGridView 需要 10 多秒。

【问题讨论】:

    标签: c# winforms linq entity-framework entity-framework-6


    【解决方案1】:

    您可以尝试将查询推送到服务器,方法是不对先前的查询调用 ToList

    var modules = context.Modules.Include("Location")
        .Where(e => e.PartNumber == PartNumber && 
                    e.Location.WarehouseType != "Site")
        .Select(a => a.Location.WarehouseName);
        //.ToList();
    
    var q = from x in modules
            group x by x into g
            let count = g.Count()
            orderby count descending
            select (g.Key + " (" + count + ")").ToString();
    

    或者只是将分组和计数合并到一个查询中:

    var modules = context.Modules.Include("Location")
        .Where(e => e.PartNumber == PartNumber && 
                    e.Location.WarehouseType != "Site")
        .GroupBy(a => a.Location.WarehouseName);
        .Select(g => g.Key + " (" + g.Count() + ")");
    

    编辑

    由于您正在处理 EF,它不能直接将您的投影转换为 SQL,您的下一个赌注是在 SQL 中进行分组并在 Linq-to-Objects 中进行字符串连接:

    var modules = context.Modules.Include("Location")
        .Where(e => e.PartNumber == PartNumber && 
                    e.Location.WarehouseType != "Site")
        .GroupBy(a => a.Location.WarehouseName);
        .Select(g => new {g.Key, Count = g.Count()})
        .AsEnumerable() // shift to linq-to-objects
        .Select(g => g.Key + " (" + g.Count + ")");
    

    【讨论】:

    • 在使用您提供的两种解决方案时,我得到一个异常(相同的异常)。错误是 System.NotSupportedException: Unable to cast the type 'System.Int32' to type 'System.Object'. LINQ to Entities only supports casting EDM primitive or enumeration types. 当我删除 ToList() 时出现此错误,正如您之前指出的那样,但它适用于我的初始代码
    • 我已经阅读了一些关于此类错误的类似帖子,我意识到问题出在 `.Select(g => g.Key + " (" + g.Count( ) + ")");` 代码。如果我只选择键或计数,那么我会返回结果,但连接 g.Key 和 g.Count 会导致错误。
    【解决方案2】:

    这是您需要正确学习有关 GroupBy()、Count() 和 OrderBy() 的所有内容,您不一定需要使用 var 的查询。一切都可以通过链接 EF 函数来完成,请参阅:

    https://code.msdn.microsoft.com/101-LINQ-Samples-3fb9811b

    【讨论】:

      【解决方案3】:

      @DStanley 是对的,此时您不应该调用 ToList 方法,因为它会立即执行您的第一个查询,而第二部分(分组和选择)将使用 Linq to Objects 在内存中执行。如果您合并两个查询,您可以在 MySql 数据库上远程执行您需要的所有操作,因此,这将提高性能:

      var q = from x in context.Modules.Include("Location")
              where x.PartNumber == PartNumber && x.Location.WarehouseType != "Site"
              group x by  x.Location.WarehouseName into g
              let count = g.Count()
              orderby count descending
              select g.Key + " (" + count + ")";
      

      此时如果要将结果带入内存,可以调用ToList方法:

      var distincItems=q.ToList();
      

      【讨论】:

      • 我收到一个错误,与使用@D Stanley 代码时遇到的相同错误。 System.NotSupportedException: Unable to cast the type 'System.Int32' to type 'System.Object'. LINQ to Entities only supports casting EDM primitive or enumeration types.我错过了什么?
      • 尝试删除ToString 电话,我已编辑我的答案
      猜你喜欢
      • 1970-01-01
      • 2011-10-07
      • 1970-01-01
      • 2019-02-11
      • 1970-01-01
      • 2010-10-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多