使用AutoMapper映射后EF Core实体变为Detached状态,无法保存更改
问题原因与解决方案
确实和你的映射方式直接相关。
问题根源
你代码中users = _mapper.Map(UsersInput, users);这一行是核心问题:AutoMapper对集合执行映射时,默认会创建一个全新的集合实例,而非修改原集合中已被DbContext跟踪的实体对象。
原来的users是从_dbContext.Users.ToListAsync()查询得到的,所有实体都处于Unchanged状态(被DbContext跟踪)。但映射后你将变量指向了AutoMapper生成的新集合,这个新集合里的实体都是未被DbContext跟踪的新对象,因此状态变为Detached,SaveChangesAsync()自然无法识别这些变化。
解决方案
推荐以下三种可行方案,按可靠性排序:
1. 逐个映射实体(最稳妥)
循环匹配输入模型与原跟踪实体,将输入属性映射到原实体上,保持原实体的跟踪状态:
public async Task<IActionResult> OnPostAsync() { var users = await _dbContext.Users.ToListAsync(); // 假设User有唯一标识Id用于匹配 foreach (var input in UsersInput) { var existingUser = users.FirstOrDefault(u => u.Id == input.Id); if (existingUser != null) { // 映射到已被跟踪的原实体,状态会自动变为Modified _mapper.Map(input, existingUser); } } await _dbContext.SaveChangesAsync(); // 正常保存修改 return Page(); }
2. 配置AutoMapper复用原集合
通过自定义映射配置,让AutoMapper在集合映射时复用原集合实例(需确保集合为可修改类型如List<T>):
// 在AutoMapper配置中添加 CreateMap<User, UserInput>().ReverseMap(); CreateMap<List<User>, List<UserInput>>() .ReverseMap() .ForMember(dest => dest, opt => opt.MapFrom((src, dest) => dest));
之后原代码中的users = _mapper.Map(UsersInput, users);会修改原集合内的实体,而非创建新集合。
3. 手动附加实体并设置状态
如果坚持使用集合映射,需手动将新集合中的实体附加到DbContext并标记为修改状态:
public async Task<IActionResult> OnPostAsync() { var users = await _dbContext.Users.ToListAsync(); users = _mapper.Map(UsersInput, users); foreach (var u in users) { _dbContext.Attach(u); _dbContext.Entry(u).State = EntityState.Modified; } await _dbContext.SaveChangesAsync(); return Page(); }
注意:此方式需确保实体主键正确,且没有重复跟踪的实体,否则可能引发冲突。
内容的提问来源于stack exchange,提问作者Mads
相关产品推荐
相关产品推荐

