C# 是否在LINQ中使用AsEnumerable()?

C# 是否在LINQ中使用AsEnumerable()?,c#,linq,linq-to-sql,entity-framework-5,linq-to-objects,C#,Linq,Linq To Sql,Entity Framework 5,Linq To Objects,我知道AsEnumerable用于从LINQ到SQL再到LINQ到Object的切换,所以我们可以在LINQ查询中使用一些额外的主要是用户定义的方法。但根据我的经验,使用AsEnumerable会使查询速度慢得多。在这种情况下,我可以稍后枚举列表以应用我自己的方法,但结果仍然非常缓慢 有人能提出更好的方法吗 这是我正在尝试做的一些代码示例 可计算的: var Data = (from r in _context.PRD_ChemProdReq //wher

我知道AsEnumerable用于从LINQ到SQL再到LINQ到Object的切换,所以我们可以在LINQ查询中使用一些额外的主要是用户定义的方法。但根据我的经验,使用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);
        }
没有可计数的:

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不直接执行查询,但在AsEnumerable之后应用where或orderby意味着Sql将获取所有项,然后对内存中的集合应用筛选和排序

简言之:

不带AsEnumerable=在SQL中完成筛选和排序 使用AsEnumerable,然后将Where或orderby=应用于带到内存中的整个集合。 您只能在内存中的集合上运行用户定义的函数,因为Linq to SQL将无法将函数解释为SQL代码。因此,您的第二个代码段没有可计算性可能是最好的


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

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

虽然这些接口在幕后的实现方式完全不同,但IEnumerable将在内存中处理您的集合,而IQueryable将把查询传递给底层数据提供程序。您可以想象,如果一个数据库表包含数百万条记录,并且您尝试对其进行排序,那么DB server可以使用索引非常快速地进行排序,这样Queryable就会大放异彩。对于IEnumerable,所有数据都需要加载到计算机内存中并在那里进行排序

要获得更长的答案,请搜索IEnumerable IQueryable difference on SO,您将看到大量详细信息:

更新:如果从第二个示例中删除call.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);
}

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

好的,伙计们,我注意到了你们所有人的不同观点,并提出了以下建议:

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。它使事情变得更快。谢谢大家。

XY问题。真正的问题在于将显示逻辑与数据访问逻辑混为一谈。这两个代码段之间没有太大区别,您总是将数据放入内存,一次使用AsEnumerable,然后使用ToList,另一次使用ToList。只有分别使用IEnumerable和IQueryable时,才能找到差异。IQueryable将通过数据提供程序帮助执行查询。sEnumerable将所有记录放入内存,然后应用过滤器,这就是为什么我们可以在选择块中使用用户定义函数,而不使用aEnumerable,在选择块中使用任何用户定义的方法调用都会引发错误。@Jain,你说得对,我很抱歉,AsEnumerable不会执行将结果集带到内存中的查询,但是任何进一步的语句或过滤器都将应用到内存中的结果集。我已经相应地更新了答案。我可以理解你的观点,但在我的情况下,什么是使事情更快的正确方法呢?如果我不使用AsEnumerable,我就不能在select块中使用用户定义的方法调用,所以我以后必须枚举,如果我使用AsEnumerable,所有记录都会进入内存,这也不好。@Jain通常使用的技术是选择所需的字段,然后在foreach中格式化它们。这就是你在第二个例子中所做的。@xanatos,你是说这比使用.AsEnumerable要好,对吗?我会尽力去做的。谢谢。你还在把DAO和显示逻辑混在一起。select中的所有代码都应该在您的UI中!这真是糟糕的软件设计。@Jain,你需要问自己一个问题-我是否担心每次运行此查询时都会加载大量的记录?如果是的话,这种方法无论如何也解决不了你的问题
Y如果你不担心的话,那么你可以选择。一个可计算的。@Aron我知道。但是这个项目已经以这种方式开发了,所以我只有很少的选择。但我们肯定会在今后的更新中努力改进。@Alexey你是对的。我们将不得不在某个时候实现服务器分页,目前我将使用.AsEnumerable。