这是我的代码,当我想更新我的featureDisa实体我得到这个错误,我不知道为什么会发生这种情况。有一点是我不能使用AsNoTracking(),因为我需要记录一些东西
var updateFeatureDisas = await _featureDisaRepository.
GetAll()
.Where(a => featureDisaIdInsert.Contains(a.DisadvantageId) &&
featureIdInsert.Contains(a.FeatureId)
&& featureParentIdInsert.Contains(a.ParentFeatureId)).ToListAsync();
var sqInsertFeatureDisa = insertTagDisa.Where(a => updateFeatureDisas.All(d => d.Id != a.Id)).ToList();
if (updateFeatureDisas.Any())
{
var sqUpdateFeatureDisa = insertTagDisa.Where(a => updateFeatureDisas.
All(d => d.Id.Equals(a.Id, StringComparison.Ordinal))).ToList();
var updateFeatureDisa = _mapper.Map<List<FeatureDisa>>(sqUpdateFeatureDisa);
_featureDisaRepository.UpdateRangeWithOutSaveChangeAsync(updateFeatureDisa);
}
这是我的UpdateWithOutSaveChangeAsync
public void UpdateWithOutSaveChangeAsync(TEntity entity)
{
DbContext.Update(entity);
}
1条答案
按热度按时间46scxncf1#
EF更改跟踪与引用一起工作。从DbContext加载实体时,默认情况下EF会跟踪该引用。如果您稍后创建具有相同ID的实体的另一个副本,并告诉DbContext
Update
该行,EF将首先检查其跟踪列表,如果发现已经在跟踪具有相同ID的实体的另一个副本,则抛出此错误。这段代码:
是你的问题Automapper正在构造新的FeatureDisa实体示例,您正试图告诉EF更新行,但您已经读取并跟踪了实体。
解决方案1:告诉EF在加载特征时不要跟踪引用。如果您的Repository方法返回
IQueryable
,并且在查询尚未在Repository中具体化的情况下正确执行了此操作,那么:这个*应该起作用,但有一个很大的警告要注意。当处理注入的DbContext时,这一个语句将加载所请求的FeatureDisas而不将它们添加到跟踪缓存中,但是在该DbContext的生命周期范围内的其他代码/调用(即请求)可以加载并跟踪一个或多个FeatureDisa实体。如果有人不小心并意识到这种依赖性,这肯定会有间歇性异常或破坏性更改的风险。
选项1a:为了确保上面的代码是“安全的”,我们需要确保在调用“Update”之前DbContext没有跟踪任何示例。如果是的话,让它分离。最好在UpdateWithoutSaveChangesAsync方法中这样做,但是作为泛型方法,它将无法工作,因为我们需要按ID查找跟踪缓存中的任何现有项。
不幸的是,代码不能工作,因为我们不能在泛型中这样做,除非我们有一个公共的基类型来访问我们可以强制转换到的ID。因此,在调用Update之前,必须在FeatureDisaRepository. UpdateRangeWithOutSaveChangeAsync()方法中完成:
假设UpdateRangeWithoutSaveChangeAsync本身是一个泛型实现:类似于:
这基本上是从我的头上跳出来的,取决于您的存储库是如何实现的,由于泛型模式,它可能会变得更加混乱。(出于这样的原因,我真的不推荐使用通用存储库/w EF)
选项2:幸运的是,有一个比Automapper可以提供的所有选项更好的选项。Automapper可以在引用之间复制值,因此保留原始读取调用加载跟踪的引用,而不是:
使用automapper的copy mapping方法调用:
不需要在存储库中调用"Update"方法,剩下的就是等待DbContext.SaveChanges()调用,修改后的FeatureDisa示例将被持久化。请注意,在这个选项中,我们***不想***将
AsNoTracking()
添加到初始查询中以获取updateFeatureDisa集合。我们想要跟踪参考,这样我们就可以让EF做它的事情。Automapper关于复制Mapper. map调用的文档在过去相当缺乏。它对于像这样的更新操作非常有用。
您可以添加到此选项的唯一其他详细信息是,在从导入DTO到FeatureDisaMap设置Automapper配置时,将其配置为仅复制您打算允许更改的值。目前,您正在使用Map器从DTO构建新的FeatureDisa,但由于我们正在更新跟踪的加载记录,因此您可以更悲观地保护更新,仅允许您打算更新的值,以保护数据免受潜在篡改数据的影响。这意味着您的更新DTO可以简化为仅反映可以/应该更改的值,因为您不再需要将构建完整FeatureDisa记录所需的所有字段传递给
Update
。