使用AsNoTracking时AutoMapper与EF循环关联映射异常问题
我之前也碰到过一模一样的问题,刚好可以分享下解决思路和最终的方案:
问题重现
在使用Entity Framework 6结合AutoMapper 6时,尝试通过.AsNoTracking()从数据库预加载Caller模型,映射到ViewModel时抛出异常。
模型结构(简化版)
Caller与Address是多对一关系:
public class Caller { public Guid Id { get; set; } public string FirstName { get; set; } public Address Address { get; set; } } public class Address { public Guid Id { get; set; } public string City { get; set; } public virtual ICollection<Caller> Callers { get; set; } }
初始映射配置
// Address CreateMap<Address, AddressViewModel>() .ForMember(vm => vm.Id, map => map.MapFrom(m => m.Id)) .ForMember(vm => vm.CallerViewModels, map => map.MapFrom(m => m.Callers)) // 问题点 .ForMember(vm => vm.City, map => map.MapFrom(m => m.City)) .ReverseMap(); // Caller CreateMap<Caller, CallerViewModel>() .ForMember(vm => vm.Id, map => map.MapFrom(m => m.Id)) .ForMember(vm => vm.AddressViewModel, map => map.MapFrom(m => m.Address)) .ForMember(vm => vm.FirstName, map => map.MapFrom(m => m.FirstName)) .ReverseMap();
仓储查询方法
public async Task<Caller> GetFullCallerAsNoTrackingAsync(Guid id) { return await _context.Callers .AsNoTracking() .Include(c => c.Address) .FirstOrDefaultAsync(c => c.Id == id); }
报错信息
Error Mapping Types ... Caller.Address -> CallerViewModel.Address ... When an object is returned with a NoTracking merge option, Load can only be called when the EntityCollection or EntityReference does not contain objects.
移除.AsNoTracking()后可正常运行,但出于性能考虑希望保留该配置,且仅需Caller -> Address的正向关联,不需要反向的Address -> Callers关联。
问题原因
核心问题在于:AutoMapper在映射Address到AddressViewModel时,尝试访问Address.Callers这个反向导航属性。由于我们用了.AsNoTracking(),EF不会跟踪这些实体,当访问未预加载的反向导航属性时,EF会尝试触发延迟加载,但NoTracking模式下不允许这种操作,因此抛出异常。
解决方法
修改AutoMapper配置,忽略那些不需要的反向导航属性,只保留需要的正向关联映射:
修改后的Address映射
CreateMap<Address, AddressViewModel>() .ForMember(vm => vm.Id, map => map.MapFrom(m => m.Id)) .ForMember(vm => vm.CallerViewModels, map => map.Ignore()) // 忽略反向导航属性的映射 .ForMember(vm => vm.City, map => map.MapFrom(m => m.City)) .ReverseMap();
修改后的Caller映射(针对额外的CallDetail关联)
CreateMap<Caller, CallerViewModel>() .ForMember(vm => vm.Id, map => map.MapFrom(m => m.Id)) .ForMember(vm => vm.AddressViewModel, map => map.MapFrom(m => m.Address)) .ForMember(vm => vm.CallDetailViewModels, map => map.Ignore()) // 同样忽略反向导航属性 .ForMember(vm => vm.FirstName, map => map.MapFrom(m => m.FirstName)) .ReverseMap();
总结规律
当使用.AsNoTracking()加载实体时,若AutoMapper的映射配置中包含反向导航属性(比如Address.Callers、Caller.CallDetails这类从子实体指向父/关联实体的集合属性),且这些属性没有被提前Include加载,EF会尝试触发延迟加载,但NoTracking模式下不允许此操作,从而引发报错。
解决的核心就是:在AutoMapper配置中,明确忽略那些业务上不需要的反向导航属性的映射,只保留需要的正向关联即可。
内容的提问来源于stack exchange,提问作者Ryan Taite

