为何直接遍历csv.GetRecords<TEntity>()结果失败,ToList()可解决?
问题原因:为什么直接遍历
csv.GetRecords<TEntity>()失败,加ToList()就成功? 这事儿核心在于延迟执行和资源释放时机的冲突,咱们一步步拆解:
1. CsvHelper的GetRecords<T>是延迟执行的
csv.GetRecords<TEntity>()返回的是IEnumerable<TEntity>,这个接口的特点是不会立即读取CSV内容——它只是创建了一个"读取计划",只有当你真正开始遍历(比如foreach、调用ToList())的时候,才会去实际读取文件里的数据。
2. using块会自动释放资源
看你的GetData方法,StreamReader和CsvReader都放在using块里:
using (var reader = new StreamReader(fileName)) using (var csv = new CsvReader(reader)) { // 配置代码... var rows = csv.GetRecords<TEntity>(); // 这里只是拿到了读取计划,没读数据 return rows; }
当方法执行到return的时候,using块的代码就结束了,.NET会自动调用reader和csv的Dispose()方法,把文件流、读取资源都释放掉。
3. 直接遍历的时候,资源已经没了
测试方法里的这段代码:
var rows = DbData.GetData<EP3_RASGEMIDDELDEN>(BasePath); foreach (var row in rows) { } // 这里才开始尝试读取数据
等你执行foreach的时候,原来的StreamReader和CsvReader早就被using块释放了,这时候再尝试读取CSV,自然会抛出类似ObjectDisposedException的错误——因为读取数据需要的资源已经不存在了。
4. ToList()为什么能解决问题?
当你调用.ToList()的时候:
var rows = csv.GetRecords<TEntity>().ToList();
ToList()会立即遍历整个IEnumerable,在using块还没结束、资源还活着的时候,就把所有CSV数据读取到内存里的List<TEntity>中。之后返回的是这个内存中的列表,foreach遍历的是已经存在于内存里的数据,完全不需要再访问已经释放的文件流,所以就不会报错。
额外补充
- 不止
ToList(),ToArray()或者任何能立即执行遍历的方法都能解决这个问题,核心就是要在using块内部完成数据的读取。 - CsvHelper设计
GetRecords为延迟执行是为了内存效率——处理超大CSV时,不用一次性把所有数据加载到内存,而是边遍历边读取。但如果要把这个可枚举对象带出using块,就必须提前把数据加载到内存里。
内容的提问来源于stack exchange,提问作者Paul Sinnema
相关产品推荐
相关产品推荐

