C# C Linq动态计算列表中项目的有效方法
我正在使用Linq对实体和EF,并希望有一个有效的方法来做到这一点。我正在做的是遍历一个列表,计算列表中不同的项,将计数附加到一个元素上,然后使用string.Join返回一个字符串。我想要和实现的是这一点 一三二一三二 从包含如下项的列表中 一,三,一,三,二,一 如果我没有从我的POCO类中进行检索,并对数据库中的每个条目进行动态处理,并将列表传递给我的DataGridView,那么这会更简单 我的代码是这样的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
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();