C# Linq Select用Expression<Func>报Value cannot be null参数source错误
问题根因
- 你所有操作都作用在
IQueryable类型上,这类查询会被EF/EF Core直接翻译成SQL在数据库端执行,而非在本地内存运行:- 第一个Select是基于数据库映射实体
User的投影,EF可以正常解析成SQL - 第二个Select的输入是自定义DTO
SearchPersonDto,数据库完全不识别这个类型的结构,EF无法解析对应表达式树,要么翻译失败,要么访问空关联属性时抛出异常
- 第一个Select是基于数据库映射实体
- 你访问
entity.Manager.Id时没有做空判断:如果数据库中User对应的Manager外键为null,无论是SQL执行阶段还是内存运行阶段,都会触发空引用错误 - 无法调试的原因:
IQueryable上的表达式树不会直接运行你写的C#委托逻辑,而是被翻译成SQL,所以断点无法命中
解决方案
方案1:合并两次投影(推荐)
直接从User实体投影到最终的SearchPerson模型,全程基于数据库实体,EF可以正常翻译,性能最优:
// 新增直接映射到ViewModel的方法 public static Expression<Func<User, SearchPerson>> ToViewModel(bool hasSensitiveAccess) { return (User entity) => new SearchPerson { Id = entity.Id, UserGuid = entity.UserGuid, Name = User.GetDisplayName(entity.KnownAs, entity.Forename, entity.Surname), // 增加null判断避免Manager为空时报错 Manager = entity.Manager != null ? new Manager() { Id = entity.Manager.Id } : null, Subordinates = new List<Subordinate>() }; } // 主方法直接调用 return await query .Select(UserSearchMappingSelector.ToViewModel(hasSensitiveAccess)) .OrderBy(user => user.Name) .ToListAsync();
方案2:先加载到内存再做二次投影
如果必须保留中间DTO的逻辑,可以先将第一次投影的结果拉到内存,后续操作在本地执行,同时支持断点调试:
// 第二个映射器里先加null判断 public static Expression<Func<SearchPersonDto, SearchPerson>> ToViewModelFromDto(bool hasSensitiveAccess) { return (SearchPersonDto entity) => new SearchPerson { Id = entity.Id, UserGuid = entity.UserGuid, Name = User.GetDisplayName(entity.KnownAs, entity.Forename, entity.Surname), Manager = entity.Manager != null ? new Manager() { Id = entity.Manager.Id } : null, Subordinates = new List<Subordinate>() }; } // 主方法中转为异步枚举后再二次映射 return await query .Select(UserSearchMappingSelector.ToDto(hasSensitiveAccess)) .AsAsyncEnumerable() .Select(UserSearchMappingSelector.ToViewModelFromDto(hasSensitiveAccess).Compile()) .OrderBy(user => user.Name) .ToListAsync();
内容的提问来源于stack exchange,提问作者Mukil Deepthi
相关产品推荐
相关产品推荐

