使用Entity Framework更新现有条目遇问题,请求技术协助
你遇到的问题核心在于EF的实体跟踪机制——直接把result = product只是改变了变量的引用,并没有修改EF上下文正在跟踪的原始实体,所以Save()时不会生成数据库更新语句。下面给你详细的解决方案和原理说明:
为什么result = product没用?
当你执行var result = _productRepository.Query().FirstOrDefault(x => x.Id == product.Id)时,EF上下文已经在跟踪result这个实体对象。而你传入的product是从HTTP请求绑定过来的,属于未被上下文跟踪的对象。把result赋值为product,只是让这个变量指向了新的对象,但EF跟踪的还是原来的result,没有任何属性变化,自然不会更新数据库。
正确的更新方式
方式1:手动映射属性(最稳妥,适合大多数场景)
把product的属性逐个赋值给EF跟踪的existingProduct,这样EF会自动检测到实体变化,Save()时就会生成更新语句:
[HttpPost] public IActionResult Edit(Product product) { if (ModelState.IsValid) { var existingProduct = _productRepository.Query().FirstOrDefault(x => x.Id == product.Id); if (existingProduct == null) return RedirectToAction("Index", "Products"); // 手动将传入的属性值映射到已跟踪的实体 existingProduct.Name = product.Name; existingProduct.Price = product.Price; existingProduct.Description = product.Description; // 其他需要更新的属性都要手动赋值 // 这里不需要调用Update,因为EF已经跟踪了existingProduct的变化 _productRepository.Save(); return View("Edit", existingProduct); } // ModelState无效时,返回视图并保留用户输入 return View(product); }
方式2:使用EF的CurrentValues.SetValues简化映射
如果product包含了所有需要更新的属性,可以用EF内置的方法批量赋值,避免手动写一堆属性:
// 替换手动赋值的部分 _context.Entry(existingProduct).CurrentValues.SetValues(product); _productRepository.Save();
注:如果你的Repository封装了DbContext,需要确保能访问到Entry方法,或者在Repository里添加对应的方法。
方式3:检查你的RepositoryUpdate方法实现
很多自定义Repository的Update方法是这样实现的:
public void Update(Product entity) { _context.Entry(entity).State = EntityState.Modified; }
这种情况下,你不能直接传入未被跟踪的product(因为上下文已经在跟踪existingProduct,会引发冲突),而是应该把existingProduct传入Update,或者先 detach 原来的实体再附加新的。但这种方式不如前两种稳妥,容易引发并发问题。
额外优化建议
如果项目里有大量类似的实体映射操作,可以使用AutoMapper这类工具简化代码,比如:
// 提前配置好映射规则 _mapper.Map(product, existingProduct);
内容的提问来源于stack exchange,提问作者user9248102

