简体   繁体   English

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

[英]C# Linq efficient way to count items on a List on the fly

I am using Linq to Entities with EF and want an efficient way to do this. 我正在使用带有EF的Linq to Entities,并且想要一种有效的方法来做到这一点。 I what am doing is going through a list, counting distinct items in the list, appending the count on one element and return one string using String.Join. 我正在做的是遍历一个列表,对列表中的各个项目进行计数,将计数附加到一个元素上,然后使用String.Join返回一个字符串。 What I want (and I achieve) is this 我想要(我实现)的是

One (3), Two(1), Three(2) 一(3),二(1),三(2)

From a List having the items like this 从具有此类项目的列表中

One, Three, One, Three, Two, One 一,三,一,三,二,一

This would have been simpler had I not been retrieving from my POCO classes and processing all this on the fly for each entry in my db and passing the List to my DataGridView. 如果我没有从POCO类中检索并为数据库中的每个条目动态处理所有这些操作,并将List传递给DataGridView,则这将更加简单。

My code is like this, 我的代码是这样的,

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; }
}

And another 还有一个

public class Warehouse
{
       //Other fields here

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

}

Then here is the POCO class where I retrieve the list and for each entity, I come up with a single string thats bound to my datagridview. 然后是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);
        }
    }

} }

It is this read-only Location property that I would like to improve its efficiency. 我想提高其效率,就是这个只读的Location属性。 My database (MySql) will hold less than 7000(maybe 2000 at most Part entities, 2000 warehouse entities and at most 5000 module entities) 我的数据库(MySql)将容纳少于7000个(也许最多为2000个Part实体,2000个仓库实体和最多5000个模块实体)

If I can improve performance a little bit I would appreciate. 如果可以稍微提高性能,我将不胜感激。 It takes more than 10 seconds to load part entities to the DataGridView. 将零件实体加载到DataGridView需要10秒钟以上。

You could try pushing the query to the server by not calling ToList on the prior query: 您可以尝试通过不调用先前查询的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();

Or just merge the grouping and counting into one query: 或者只是将分组和计数合并为一个查询:

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() + ")");

EDIT 编辑

Since you are dealing with EF, which cannot directly translate your projection to SQL, your next bet would be to geep the grouping in SQL and do the string concatenation in Linq-to-Objects: 由于您正在处理EF,而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 + ")");

This is everything you need to learn correctly about GroupBy(), Count() and OrderBy(), you don't absolutely need a query that uses var. 这就是您需要正确学习GroupBy(),Count()和OrderBy()的所有内容,您不一定需要使用var的查询。 Everything can be done by chaining EF functions, see : 一切都可以通过链接EF函数来完成,请参阅:

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

@DStanley is right, you should not call the ToList method in that point because it will execute immediately your first query and the second part (grouping and selecting) would be executed in memory using Linq to Objects. @DStanley是正确的,您此时不应调用ToList方法,因为它将立即执行您的第一个查询,而第二部分(分组和选择)将使用Linq to Objects在内存中执行。 If you merge both query you can execute all that you need remotely on your MySql database, so, that would improve the performance: 如果合并两个查询,则可以在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 + ")";

At this point if you want to bring the result to memory, you can call the ToList method: 此时,如果要将结果存储到内存中,可以调用ToList方法:

var distincItems=q.ToList();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM