AsEnumerable以及它如何影响yield和SqlDataReader

时间:2012-03-06 13:56:56

标签: c# linq ienumerable yield-return

我试图了解AsEnumerable()在迭代它时对我的数据的影响。我有一个模拟内存列表。如果我foreach首先调用ToList(),这会强制进行评估,我的打印输出看起来像这样(请参阅本文底部的代码来解释输出):

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,我的问题就是:

  1. 为什么IQueryable对于内存集合与linq到sql及其SqlDataReader返回类型的行为不同?

  2. 当我的存储库更改为使用yield return并在阅读器内部执行Read()(同时调用foreach方法)时,所有这些都会发生变化。在执行yield之前,来自SqlServer的在客户端网络缓冲区中缓冲的行是否会被完全评估(通常这里foreach会导致repo中的“暂停”,而每个行都由ToList()阻止。我知道如果我在这种情况下首先致电SqlDataReader,我可以强制评估AsEnumerableSqlDataReader也会这样做吗?

  3. 注意:我不知道将收益率放到 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"); } } 是否是一个好主意,因为它可能会保持连接打开,我已经把这个话题打败了:)

    这是我的测试代码:

    {{1}}

3 个答案:

答案 0 :(得分:4)

除了将表达式类型更改为AsEnumerable之外,

IEnumerable<T> nothing 除外。当它在这样的查询中使用时:

var query = db.Customers
              .Where(x => x.Foo)
              .AsEnumerable()
              .Where(x => x.Bar);

...这只是意味着你将Queryable.Where用于第一个谓词(以便转换为SQL),而Enumerable.Where用于第二个谓词(这样在.NET代码中执行)

它不会强制评估。它没有任何。它甚至不会检查是否在null上调用它。

有关详细信息,请参阅我的Edulinq blog post on AsEnumerable

答案 1 :(得分:1)

@Jon Skeet已经发布了AsEnumerable()所做的事情 - 它只是改变了编译时间类型。但你为什么要用呢?

基本上,通过将表达式从IQueryable更改为IEnumerable,您现在可以使用Linq到对象(而不是数据库提供程序的IQueryable实现),没有任何限制 - 不必是数据库方面的等效方法,因此您可以自由地执行对象转换,远程调用(如果需要)或任何类型的字符串操作。

那就是说当你还在使用数据库(IQueryable)时你会想要做所有的过滤 - 否则你会把所有这些行都带到内存中,这会耗费你的成本 - 然后才使用AsEnumerable()之后进行最后的转换。

答案 2 :(得分:0)

根据MSDN documentation

  

AsEnumerable(Of TSource)(IEnumerable(Of TSource))方法没有   效果除了从a更改源代码的编译时类型   将IEnumerable(Of T)实现为IEnumerable(Of T)本身的类型。

它不应该引起任何评估,只是提示您要使用IEnumerable方法与其他一些实现(IQueryable等)。