问题描述:在IEnumerable使用时显示警告
分析:如果对IEnumerable多次读取操作,会有因数据源改变导致前后两次枚举项不固定的风险,最突出例子是读取数据库的时候,第二次foreach时恰好数据源发生了改变,那么读取出来的数据和第一次就不一致了。
查看测试代码
几乎所有返回类型为 IEnumerable<T> 或 IOrderedEnumerable<TElement> 的标准查询运算符都以延迟方式执行。如下表我们可以看到where时,返回的IEnumerable是延迟加载的。
标准查询运算符 |
Return Type |
立即执行 |
延迟流式执行 |
延迟非流式执行 |
TSource |
√ |
|||
√ |
||||
√ |
||||
√ |
||||
单个数值 |
√ |
|||
√ |
||||
√ |
||||
√ |
||||
√ |
||||
√ |
||||
√ |
||||
TSource |
√ |
|||
TSource |
√ |
|||
√ |
||||
√ |
√ |
|||
TSource |
√ |
|||
TSource |
√ |
|||
√ |
||||
√ |
√ |
|||
√ |
√ |
|||
√ |
√ |
|||
TSource |
√ |
|||
TSource |
√ |
|||
√ |
||||
单个数值、TSource 或 TResult |
√ |
|||
单个数值、TSource 或 TResult |
√ |
|||
√ |
||||
√ |
||||
√ |
||||
√ |
||||
√ |
||||
√ |
||||
√ |
||||
√ |
||||
√ |
||||
TSource |
√ |
|||
TSource |
√ |
|||
√ |
||||
√ |
||||
单个数值 |
√ |
|||
√ |
||||
√ |
||||
√ |
||||
√ |
||||
TSource 数组 |
√ |
|||
√ |
||||
√ |
||||
√ |
||||
√ |
||||
√ |
解决方案:
多次使用IEnumerable时,最好转换为List或者Array
测试代码:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading; 6 using System.Threading.Tasks; 7 using ConsoleApplication2.EF; 8 9 namespace ConsoleApplication2 10 { 11 class Program_IEnumerable 12 { 13 static void Main(string[] args) 14 { 15 // 异步访问数据库 16 Task.Run(() => 17 { 18 while (true) 19 { 20 reloadDb(); 21 } 22 }); 23 24 // 使用死循环不停的读取数据 25 int count = 1; 26 while (true) 27 { 28 Console.WriteLine("第{0}读取", count); 29 IEnumerable<string> names = getNames(); 30 31 var allNames = new StringBuilder(); 32 foreach (var name in names) 33 allNames.Append(name + ","); 34 Thread.Sleep(500); 35 36 var allNames2 = new StringBuilder(); 37 foreach (var name in names) 38 allNames2.Append(name + ","); 39 if (allNames2 != allNames) 40 Console.WriteLine("数据源发生了改变"); 41 count++; 42 43 Thread.Sleep(1000); 44 } 45 46 Console.ReadKey(); 47 } 48 49 static void reloadDb() 50 { 51 using (var infosEntities = new TestEntities()) 52 { 53 infosEntities.Student.Add(new Student 54 { 55 EnrollmentDate = DateTime.Now, 56 FirstMidName = "han", 57 LastName = "zhu" 58 }); 59 infosEntities.SaveChanges(); 60 } 61 Thread.Sleep(1000); 62 63 using (var infosEntities = new TestEntities()) 64 { 65 var entity = infosEntities.Student.FirstOrDefault(a => a.FirstMidName == "han"); 66 if (entity != null) 67 { 68 infosEntities.Student.Remove(entity); 69 infosEntities.SaveChanges(); 70 } 71 } 72 Thread.Sleep(1000); 73 } 74 75 static IEnumerable<string> getNames() 76 { 77 var infosEntities = new TestEntities(); 78 return infosEntities.Student.Select(a => a.FirstMidName + " " + a.LastName); 79 } 80 81 } 82 83 }
参考资料:
Resharper官方对于这个警告的描述:
https://www.jetbrains.com/help/resharper/PossibleMultipleEnumeration.html
MSDN的解释:
https://msdn.microsoft.com/zh-cn/library/vs/alm/bb882641(v=vs.90)/css