IEnumerable批量赋值RoomCode为空:单实例查询正常
问题分析与解决方案
你遇到的核心问题是IEnumerable的延迟执行特性导致的赋值无效,咱们一步步拆解:
为什么GetAll()里RoomCode始终为null?
你的result是通过deviceRepository.GetAll().Select(...)得到的IEnumerable<DeviceViewModel>,这是延迟执行的——也就是说,每次调用Count()、ElementAt(i)的时候,都会重新执行一遍Select语句,生成新的DeviceViewModel实例。
所以你在循环里做的result.ElementAt(i).RoomCode = ...,其实是给一个临时实例赋值,下一次调用ElementAt(i)又会生成新的实例,之前的赋值完全没被保留下来,最后返回的result还是原始的延迟查询,自然没有RoomCode的值。
而GetDeviceId()方法是直接创建了单个DeviceViewModel实例,赋值后直接返回,不存在延迟执行的问题,所以能正常工作。
修复方案
方案1:先转为List,避免延迟执行
把延迟执行的IEnumerable转为立即执行的List,这样操作的是同一个实例集合,赋值会被保留:
public IEnumerable<DeviceViewModel> GetAll() { // 先ToList()立即执行查询,得到实际的ViewModel集合 var result = deviceRepository.GetAll() .Select(x => x.ToViewModel<DeviceViewModel>()) .ToList(); foreach(var item in result) { item.RoomCode = deviceRepository.GetRoomCode(item.DeviceId); } return result; }
方案2:优化查询,在Select中直接关联获取RoomCode(更高效)
上面的方案虽然能解决问题,但循环里每次调用GetRoomCode都会发起一次数据库查询,数据量大的时候性能会很差。更优的方式是直接在LINQ查询里关联表,一次性获取所有需要的数据:
public IEnumerable<DeviceViewModel> GetAll() { var result = from device in deviceRepository.GetAll() join roomDevice in dbContext.Set<RoomDevice>() on device.Id equals roomDevice.DeviceId into roomDeviceGroup from rd in roomDeviceGroup.Where(x => x.IsActive == true).DefaultIfEmpty() join room in dbContext.Set<Room>() on rd?.RoomId equals room.Id into roomGroup from r in roomGroup.DefaultIfEmpty() select new DeviceViewModel { // 复用ToViewModel处理已有属性,补充RoomCode ...device.ToViewModel<DeviceViewModel>(), RoomCode = r?.RoomCode ?? "" }; return result.ToList(); // ToList()避免后续重复执行数据库查询 }
这样只需要一次数据库查询就能获取所有数据,性能比循环调用GetRoomCode好很多。
额外提示
- 尽量避免在IEnumerable上多次调用
Count()、ElementAt()这类方法,因为每次都会重新执行查询,既影响性能又容易出现你遇到的这类问题。 - 如果你的
ToViewModel是用AutoMapper之类的工具实现的,可以配置映射规则,直接在映射时关联获取RoomCode,代码会更简洁。
内容的提问来源于stack exchange,提问作者user11131551
相关产品推荐
相关产品推荐

