是否在 LINQ 中使用 AsEnumerable()?
Whether to use AsEnumerable() in LINQ or not?
我理解 AsEnumerable()
用于从 "LINQ to SQL" 切换到 "LINQ to Object",因此我们可以在 LINQ 查询中使用一些额外的(主要是用户定义的)方法。但是从我的经验来看,使用 AsEnumerable()
会使查询速度变慢。在那种情况下,我可以稍后枚举列表以应用我自己的方法,但结果仍然很慢。
谁能提出更好的方法?
这是我正在尝试做的一些代码示例?
使用 AsEnumerable():
var Data = (from r in _context.PRD_ChemProdReq.AsEnumerable()
//where r.RecordStatus == "NCF"
orderby r.RequisitionNo descending
select new PRDChemProdReq
{
RequisitionID = r.RequisitionID,
RequisitionNo = r.RequisitionNo,
RequisitionCategory = DalCommon.ReturnRequisitionCategory(r.RequisitionCategory),
RequisitionType = DalCommon.ReturnOrderType(r.RequisitionType),
ReqRaisedOn = (Convert.ToDateTime(r.ReqRaisedOn)).ToString("dd'/'MM'/'yyyy"),
RecordStatus= DalCommon.ReturnRecordStatus(r.RecordStatus),
RequisitionFromName = DalCommon.GetStoreName(r.RequisitionFrom),
RequisitionToName = DalCommon.GetStoreName(r.RequisitionTo)
}).ToList();
没有 AsEnumerable():
var Data = (from r in _context.PRD_ChemProdReq
//where r.RecordStatus == "NCF"
orderby r.RequisitionNo descending
select new PRDChemProdReq
{
RequisitionID = r.RequisitionID,
RequisitionNo = r.RequisitionNo,
RequisitionCategory = r.RequisitionCategory,
RequisitionType = (r.RequisitionType),
ReqRaisedOnTemp = (r.ReqRaisedOn),
RecordStatus= (r.RecordStatus),
RequisitionFrom = (r.RequisitionFrom),
RequisitionTo = (r.RequisitionTo)
}).ToList();
foreach (var item in Data)
{
item.RequisitionCategory = DalCommon.ReturnRequisitionCategory(item.RequisitionCategory);
item.RequisitionType = DalCommon.ReturnOrderType(item.RequisitionType);
item.ReqRaisedOn = (Convert.ToDateTime(item.ReqRaisedOnTemp)).ToString("dd'/'MM'/'yyyy");
item.RecordStatus = DalCommon.ReturnRecordStatus(item.RecordStatus);
item.RequisitionFromName = DalCommon.GetStoreName(item.RequisitionFrom);
item.RequisitionToName = DalCommon.GetStoreName(item.RequisitionTo);
}
AsEnumerable()
如果在其后添加任何查询元素,速度会变慢。
即使 AsEnumerable()
不直接执行查询,应用 where
或 orderby
after AsEnumerable()
意味着 Sql 将获得 all 项目,然后应用 filtering 和 ordering collection在记忆中。
简而言之:
- 没有
AsEnumerable()
= 在 SQL 中完成过滤和排序
- 用
AsEnumerable()
然后Where
或orderby
=应用到整个collection带入内存。
您只能 运行 内存中 collection 上的用户定义函数(因为您的 Linq to SQL 将无法将您的函数解释为 SQL 代码).所以你的第二个代码片段(没有 AsEnumerable()
)可能是最好的。
唯一的其他选择是在 SQL 本身中应用您的 用户定义的函数。
您似乎将这两个接口混淆为两个完全不同的东西。事实上 IQueryable
是从 IEnumerable
继承而来的,所以无论对你使用后者有用的东西,对前者也适用,所以不需要使用 AsEnumerable
。
虽然这些接口的实现方式完全不同,但在幕后 - IEnumerable 将在内存中处理您的集合,而 IQueryable 会将查询传递给基础数据提供程序。您可以想象,如果数据库 table 包含数百万条记录并且您尝试对其进行排序,数据库服务器可以非常快速地完成(使用索引),因此 Queryable 将大放异彩。对于 IEnumerable,所有数据都需要加载到您的计算机内存并在那里排序。
要获得更长的答案,请在 SO 上搜索 "IEnumerable IQueryable difference",您会看到很多详细信息:
Random Link 1
Random Link 2
更新: 如果您从第二个示例中删除调用 .ToList
,则结果不会自动加载到内存中。此时您需要决定要将哪些项目存储在内存中并仅为它们调用函数。
var Data = (from r in _context.PRD_ChemProdReq
orderby r.RequisitionNo descending
select new PRDChemProdReq
{
// do your initialization
});
var subsetOfData = Data.Take(100).ToList(); // Now it's loaded to memory
foreach (var item in subsetOfData)
{
item.RequisitionCategory = DalCommon.ReturnRequisitionCategory(item.RequisitionCategory);
item.RequisitionType = DalCommon.ReturnOrderType(item.RequisitionType);
item.ReqRaisedOn = (Convert.ToDateTime(item.ReqRaisedOnTemp)).ToString("dd'/'MM'/'yyyy");
item.RecordStatus = DalCommon.ReturnRecordStatus(item.RecordStatus);
item.RequisitionFromName = DalCommon.GetStoreName(item.RequisitionFrom);
item.RequisitionToName = DalCommon.GetStoreName(item.RequisitionTo);
}
现在,如果您确实需要为 all 分配这些属性,您的数据和数据可以是任意大的,您需要制定一个策略来实现它。非常简单的选择是将它们保存到新的数据库table,然后处理数据的大小将仅受数据库容量的限制。
好的伙计们,我注意到你们所有人的不同观点并想出了这个:
var Data = (from r in _context.PRD_ChemProdReq.AsEnumerable()
//where r.RecordStatus == "NCF"
join rf in _context.SYS_Store on (r.RequisitionFrom==null?0: r.RequisitionFrom) equals rf.StoreID into requisitionfrom
from rf in requisitionfrom.DefaultIfEmpty()
join rt in _context.SYS_Store on (r.RequisitionTo == null ? 0 : r.RequisitionTo) equals rt.StoreID into requisitionto
from rt in requisitionto.DefaultIfEmpty()
orderby r.RequisitionNo descending
select new PRDChemProdReq
{
RequisitionID = r.RequisitionID,
RequisitionNo = r.RequisitionNo,
RequisitionCategory = DalCommon.ReturnRequisitionCategory(r.RequisitionCategory),
RequisitionType = r.RequisitionType == "UR" ? "Urgent" : "Normal",
ReqRaisedOn = (Convert.ToDateTime(r.ReqRaisedOn)).ToString("dd'/'MM'/'yyyy"),
RecordStatus = (r.RecordStatus=="NCF"? "Not Confirmed": "Approved"),
RequisitionFromName = (rf==null? null: rf.StoreName),
RequisitionToName = (rt == null ? null : rt.StoreName)
});
首先,我删除了我的 ToList(),它只执行调用 AsEnumerable() 时已经完成的查询。两次执行相同的查询没有意义。此外,我在 select 块中的自定义方法调用也在很大程度上降低了速度。我尝试减少方法调用,而是尽可能使用连接。它使事情变得更快。谢谢大家。
我理解 AsEnumerable()
用于从 "LINQ to SQL" 切换到 "LINQ to Object",因此我们可以在 LINQ 查询中使用一些额外的(主要是用户定义的)方法。但是从我的经验来看,使用 AsEnumerable()
会使查询速度变慢。在那种情况下,我可以稍后枚举列表以应用我自己的方法,但结果仍然很慢。
谁能提出更好的方法?
这是我正在尝试做的一些代码示例?
使用 AsEnumerable():
var Data = (from r in _context.PRD_ChemProdReq.AsEnumerable()
//where r.RecordStatus == "NCF"
orderby r.RequisitionNo descending
select new PRDChemProdReq
{
RequisitionID = r.RequisitionID,
RequisitionNo = r.RequisitionNo,
RequisitionCategory = DalCommon.ReturnRequisitionCategory(r.RequisitionCategory),
RequisitionType = DalCommon.ReturnOrderType(r.RequisitionType),
ReqRaisedOn = (Convert.ToDateTime(r.ReqRaisedOn)).ToString("dd'/'MM'/'yyyy"),
RecordStatus= DalCommon.ReturnRecordStatus(r.RecordStatus),
RequisitionFromName = DalCommon.GetStoreName(r.RequisitionFrom),
RequisitionToName = DalCommon.GetStoreName(r.RequisitionTo)
}).ToList();
没有 AsEnumerable():
var Data = (from r in _context.PRD_ChemProdReq
//where r.RecordStatus == "NCF"
orderby r.RequisitionNo descending
select new PRDChemProdReq
{
RequisitionID = r.RequisitionID,
RequisitionNo = r.RequisitionNo,
RequisitionCategory = r.RequisitionCategory,
RequisitionType = (r.RequisitionType),
ReqRaisedOnTemp = (r.ReqRaisedOn),
RecordStatus= (r.RecordStatus),
RequisitionFrom = (r.RequisitionFrom),
RequisitionTo = (r.RequisitionTo)
}).ToList();
foreach (var item in Data)
{
item.RequisitionCategory = DalCommon.ReturnRequisitionCategory(item.RequisitionCategory);
item.RequisitionType = DalCommon.ReturnOrderType(item.RequisitionType);
item.ReqRaisedOn = (Convert.ToDateTime(item.ReqRaisedOnTemp)).ToString("dd'/'MM'/'yyyy");
item.RecordStatus = DalCommon.ReturnRecordStatus(item.RecordStatus);
item.RequisitionFromName = DalCommon.GetStoreName(item.RequisitionFrom);
item.RequisitionToName = DalCommon.GetStoreName(item.RequisitionTo);
}
AsEnumerable()
如果在其后添加任何查询元素,速度会变慢。
即使 AsEnumerable()
不直接执行查询,应用 where
或 orderby
after AsEnumerable()
意味着 Sql 将获得 all 项目,然后应用 filtering 和 ordering collection在记忆中。
简而言之:
- 没有
AsEnumerable()
= 在 SQL 中完成过滤和排序
- 用
AsEnumerable()
然后Where
或orderby
=应用到整个collection带入内存。
您只能 运行 内存中 collection 上的用户定义函数(因为您的 Linq to SQL 将无法将您的函数解释为 SQL 代码).所以你的第二个代码片段(没有 AsEnumerable()
)可能是最好的。
唯一的其他选择是在 SQL 本身中应用您的 用户定义的函数。
您似乎将这两个接口混淆为两个完全不同的东西。事实上 IQueryable
是从 IEnumerable
继承而来的,所以无论对你使用后者有用的东西,对前者也适用,所以不需要使用 AsEnumerable
。
虽然这些接口的实现方式完全不同,但在幕后 - IEnumerable 将在内存中处理您的集合,而 IQueryable 会将查询传递给基础数据提供程序。您可以想象,如果数据库 table 包含数百万条记录并且您尝试对其进行排序,数据库服务器可以非常快速地完成(使用索引),因此 Queryable 将大放异彩。对于 IEnumerable,所有数据都需要加载到您的计算机内存并在那里排序。
要获得更长的答案,请在 SO 上搜索 "IEnumerable IQueryable difference",您会看到很多详细信息:
Random Link 1 Random Link 2
更新: 如果您从第二个示例中删除调用 .ToList
,则结果不会自动加载到内存中。此时您需要决定要将哪些项目存储在内存中并仅为它们调用函数。
var Data = (from r in _context.PRD_ChemProdReq
orderby r.RequisitionNo descending
select new PRDChemProdReq
{
// do your initialization
});
var subsetOfData = Data.Take(100).ToList(); // Now it's loaded to memory
foreach (var item in subsetOfData)
{
item.RequisitionCategory = DalCommon.ReturnRequisitionCategory(item.RequisitionCategory);
item.RequisitionType = DalCommon.ReturnOrderType(item.RequisitionType);
item.ReqRaisedOn = (Convert.ToDateTime(item.ReqRaisedOnTemp)).ToString("dd'/'MM'/'yyyy");
item.RecordStatus = DalCommon.ReturnRecordStatus(item.RecordStatus);
item.RequisitionFromName = DalCommon.GetStoreName(item.RequisitionFrom);
item.RequisitionToName = DalCommon.GetStoreName(item.RequisitionTo);
}
现在,如果您确实需要为 all 分配这些属性,您的数据和数据可以是任意大的,您需要制定一个策略来实现它。非常简单的选择是将它们保存到新的数据库table,然后处理数据的大小将仅受数据库容量的限制。
好的伙计们,我注意到你们所有人的不同观点并想出了这个:
var Data = (from r in _context.PRD_ChemProdReq.AsEnumerable()
//where r.RecordStatus == "NCF"
join rf in _context.SYS_Store on (r.RequisitionFrom==null?0: r.RequisitionFrom) equals rf.StoreID into requisitionfrom
from rf in requisitionfrom.DefaultIfEmpty()
join rt in _context.SYS_Store on (r.RequisitionTo == null ? 0 : r.RequisitionTo) equals rt.StoreID into requisitionto
from rt in requisitionto.DefaultIfEmpty()
orderby r.RequisitionNo descending
select new PRDChemProdReq
{
RequisitionID = r.RequisitionID,
RequisitionNo = r.RequisitionNo,
RequisitionCategory = DalCommon.ReturnRequisitionCategory(r.RequisitionCategory),
RequisitionType = r.RequisitionType == "UR" ? "Urgent" : "Normal",
ReqRaisedOn = (Convert.ToDateTime(r.ReqRaisedOn)).ToString("dd'/'MM'/'yyyy"),
RecordStatus = (r.RecordStatus=="NCF"? "Not Confirmed": "Approved"),
RequisitionFromName = (rf==null? null: rf.StoreName),
RequisitionToName = (rt == null ? null : rt.StoreName)
});
首先,我删除了我的 ToList(),它只执行调用 AsEnumerable() 时已经完成的查询。两次执行相同的查询没有意义。此外,我在 select 块中的自定义方法调用也在很大程度上降低了速度。我尝试减少方法调用,而是尽可能使用连接。它使事情变得更快。谢谢大家。