AsEnumerable及其如何影响收益率和SqlDataReader
本文关键字:影响 收益率 SqlDataReader 何影响 AsEnumerable | 更新日期: 2023-09-27 18:26:47
我试图了解AsEnumerable()
在迭代数据时对数据的影响。我有一个内存中的mock列表。如果我在第一次调用ToList()
时foreach
覆盖它,这将强制评估,我的打印输出如下所示(请参阅本文底部的代码来解释输出):
entering da
yield return
yield return
yield return
exiting da
doing something to aaron
doing something to jeremy
doing something to brendan
一切都有道理。ToList()
强制存储库中的yield首先执行到列表中,然后我们得到foreach
迭代。到目前为止一切都很好。
当我除了使用AsEnumerable()
之外做同样的事情时,根据我读到的关于IQueryable
的内容(我知道这不是IQueryable
),我本以为这也会强制评估,但事实并非如此。它看起来像这样:
entering da
yield return
doing something to aaron
yield return
doing something to jeremy
yield return
doing something to brendan
exiting da
如果我从来没有打过AsEnumerable()
,那么我的问题是:
为什么
AsEnumerable
在内存中集合中的行为与linq-to-sql及其IQueryable
返回类型不同?当我的存储库被更改为使用
SqlDataReader
并在读取器内部执行yield return
时(同时调用Read()
方法),所有这些会发生什么变化。在执行foreach
之前,是否会对来自SqlServer的缓存在客户端网络缓冲区中的行进行全面评估(通常,这里的yield
会导致回购中的"暂停",而每一行都由foreach
块处理。我知道如果在这种情况下我先调用ToList()
,我可以强制评估SqlDataReader
,那么AsEnumerable
在这里也会这样做吗?
注意:我对将yield放入SqlDataReader
是否是个好主意不感兴趣,因为它可能会打开连接,我已经把这个话题打得落花流水了:)
这是我的测试代码:
public class TestClient
{
public void Execute()
{
var data = MockRepo.GetData();
foreach (var p in data.AsEnumerable()) //or .ToList()
{
Console.WriteLine("doing something to {0}", p.Name);
}
Console.ReadKey();
}
}
public class Person
{
public Person(string name)
{
Name = name;
}
public string Name { get; set; }
}
public class MockRepo
{
private static readonly List<Person> items = new List<Person>(3)
{
new Person("aaron"),
new Person("jeremy"),
new Person("brendan")
};
public static IEnumerable<Person> GetData()
{
Console.WriteLine("entering da");
var enumerator = items.GetEnumerator();
while (enumerator.MoveNext())
{
Console.WriteLine("yield return");
yield return enumerator.Current;
}
Console.WriteLine("exiting da");
}
}
AsEnumerable
除了将表达式类型更改为IEnumerable<T>
之外,什么都不做。当它用于这样的查询时:
var query = db.Customers
.Where(x => x.Foo)
.AsEnumerable()
.Where(x => x.Bar);
这意味着您将使用Queryable.Where
作为第一个谓词(因此转换为SQL),使用Enumerable.Where
作为第二个谓词(从而在.NET代码中执行)。
它不会强制评估。它对没有任何作用。它甚至不检查是否在null
上调用了它。
有关更多信息,请参阅我在AsEnumerable
上的Edulinq博客文章。
@Jon Skeet已经发布了AsEnumerable()
的功能——它只是更改了编译时类型。但你为什么要用它呢?
从本质上讲,通过将表达式从IQueryable
更改为IEnumerable
,您现在可以在没有任何限制的情况下使用Linq-to-Objects(而不是数据库提供程序的IQueryable实现)-数据库端不必有等效的方法,因此您可以自由地执行对象转换、远程调用(如果需要)或任何类型的字符串操作。
也就是说,当你还在处理数据库(IQueryable
)时,你会想做所有你能做的过滤——否则你会把所有这些行都带到内存中,这会让你付出代价——然后再使用AsEnumerable()
进行最后的转换。
根据MSDN文档:
AsEnumerable(Of-TSource)(IEnumerable(Of-TSource))方法没有除了从类型,将IEnumerable(Of T)实现为IEnumeraable(Of T)本身。
它不应该引起任何评估,只是提示您希望使用IEnumerable方法与其他实现(IQueryable等)。