关于c#:是否在LINQ中使用AsEnumerable()?

Whether to use AsEnumerable() in LINQ or not?

我知道AsEnumerable()用于从"linq to sql"切换到"linq to object",所以我们可以在linq查询中使用一些额外的(主要是用户定义的)方法。但从我的经验来看,使用AsEnumerable()会使查询速度慢得多。在这种情况下,我可以稍后枚举列表以应用我自己的方法,但结果仍然相当缓慢。

有人能提出更好的方法吗?

这是一些我要做的代码示例?

使用AsEnumerable():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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);
        }


看起来您将这两个接口混淆为两个完全不同的东西。事实上,IQueryable是从IEnumerable继承下来的,所以无论你用IEnumerable做什么,都可以用前者,所以不需要用AsEnumerable做。

尽管这些接口的实现方式非常不同,但在幕后,IEnumerable将在内存中处理您的集合,而iQueryable将把查询传递给基础数据提供程序。您可以想象,如果一个数据库表包含数百万条记录,并且您试图对其进行排序,那么DBServer可以非常快速地进行排序(使用索引),这样Queryable就会发光。对于IEnumerable,所有数据都需要加载到计算机内存中并在那里排序。

要获得更长的答案,请搜索"IEnumerable IQueryable Difference",这样,您将看到许多详细信息:

随机链接1随机链接2

更新:如果从第二个示例中删除调用.ToList,那么结果将不会自动加载到内存中。此时,您需要决定要在内存中存储哪些项,并只为它们调用函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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);
}

现在,如果您实际上需要为所有数据分配这些属性,并且数据可以是任意大的,那么您需要制定一个策略,如何做到这一点。非常简单的选择是将它们保存到数据库中的一个新表中,然后处理的数据的大小将只受数据库容量的限制。


如果在AsEnumerable()之后添加任何查询元素,它将变慢。

尽管AsEnumerable()不直接执行查询,但在AsEnumerable()之后应用whereorderby意味着SQL将获取所有项,然后对内存中的集合应用筛选和排序。

简而言之:

  • 不带AsEnumerable()=在SQL中进行过滤和排序
  • 使用AsEnumerable(),然后将whereorderby应用于带到内存中的整个集合。

只能在内存中的集合上运行用户定义的函数(因为LINQ to SQL将无法将函数解释为SQL代码)。所以您的第二个代码片段(没有AsEnumerable())可能是最好的。

唯一的另一种选择是在SQL本身中应用用户定义的函数。


好了,伙计们,我注意到你们所有人的不同观点,想出了这个:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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块中的自定义方法调用也起到了减缓速度的主要作用。我尝试减少方法调用,而在可能的情况下使用join。它使事情变得相当快。谢谢大家。