C# C Linq动态计算列表中项目的有效方法

C# C Linq动态计算列表中项目的有效方法,c#,winforms,linq,entity-framework,entity-framework-6,C#,Winforms,Linq,Entity Framework,Entity Framework 6,我正在使用Linq对实体和EF,并希望有一个有效的方法来做到这一点。我正在做的是遍历一个列表,计算列表中不同的项,将计数附加到一个元素上,然后使用string.Join返回一个字符串。我想要和实现的是这一点 一三二一三二 从包含如下项的列表中 一,三,一,三,二,一 如果我没有从我的POCO类中进行检索,并对数据库中的每个条目进行动态处理,并将列表传递给我的DataGridView,那么这会更简单 我的代码是这样的 public class Module { //Other fiel

我正在使用Linq对实体和EF,并希望有一个有效的方法来做到这一点。我正在做的是遍历一个列表,计算列表中不同的项,将计数附加到一个元素上,然后使用string.Join返回一个字符串。我想要和实现的是这一点

一三二一三二

从包含如下项的列表中

一,三,一,三,二,一

如果我没有从我的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秒以上。

您可以尝试将查询推送到服务器,方法是不调用之前查询的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中对分组进行geep,并在Linq中对对象进行字符串连接:

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函数来完成,请参阅:


@DStanley是对的,您不应该在这一点上调用ToList方法,因为它将立即执行您的第一个查询,第二部分分组和选择将在内存中使用Linq to对象执行。如果合并这两个查询,则可以在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方法:


我遇到了一个异常,当使用您提供的两个解决方案时,出现了相同的异常。错误为System.NotSupportedException:无法将类型“System.Int32”强制转换为类型“System.Object”。LINQ to实体仅支持强制转换EDM基元或枚举类型。正如你前面指出的,当我删除ToList时,我发现了这个错误,但它与我的初始代码配合得很好。我读过一些类似的帖子,关于这类错误,我意识到问题在于`.Selectg=>g.Key++g.Count+;`密码如果我只是选择键或计数,那么我会返回结果,但是连接g.key和g.count会导致错误。我会得到一个错误,与使用@D Stanley代码时得到的错误相同。System.NotSupportedException:无法将类型“System.Int32”强制转换为类型“System.Object”。LINQ to实体仅支持强制转换EDM基元或枚举类型。我遗漏了什么?请尝试删除ToString呼叫,我已编辑了我的答案
var distincItems=q.ToList();