C#Linq快速计算列表中项目的有效方法

本文关键字:项目 有效 方法 列表 计算 C#Linq | 更新日期: 2023-09-27 18:01:00

我正在使用带有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);
        }
    }

}

正是这个只读的位置属性,我想提高它的效率。我的数据库(MySql(将容纳少于7000个(可能最多2000个部件实体、2000个仓库主体和最多5000个模块实体(

如果我能稍微提高一点表现,我将不胜感激。将零件实体加载到DataGridView需要超过10秒的时间。

C#Linq快速计算列表中项目的有效方法

您可以尝试将查询推送到服务器,方法是不对前一个查询调用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中将字符串连接为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 + ")");

这是你需要正确学习的关于GroupBy((、Count((和OrderBy((的一切,你并不绝对需要使用var的查询。一切都可以通过链接EF函数来完成,请参阅:

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

@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();