C#Linq有效地计算List上的项目

时间:2015-07-02 19:25:32

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

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

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

来自具有此类项目的列表

一,三,一,三,二,一

如果我没有从我的POCO类中检索并为我的数据库中的每个条目动态处理所有这些并将List传递给我的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秒以上的时间。

3 个答案:

答案 0 :(得分:3)

您可以尝试通过不在先前查询上调用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 + ")");

答案 1 :(得分:2)

这是您正确了解GroupBy(),Count()和OrderBy()所需的一切,您绝对不需要使用var的查询。一切都可以通过链接EF函数来完成,请参阅:

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

答案 2 :(得分:2)

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