对从AutoMapper映射的实体使用AsNoTracking

时间:2019-02-28 21:01:43

标签: c# entity-framework automapper

我遇到了使用自动映射器映射DTO->数据库实体的情况。

var entityObj = _mapper.Map<REQUESTEXT>(reqDTO);

然后我正在使用entityObj更新数据库中的记录。

void Update(REQUESTEXT entityObj)
{
    _context.REQUESTEXTs.Attach(entityObj);  <--- Error
    _context.Entry(entityObj).Property(x => x.CUSTOPTIONCD).IsModified = true;
    _context.SaveChanges();
}

当我尝试将REQUESTEXT对象附加到上下文时,它给我一个错误:

  

附加类型为'A'的实体失败,因为   相同类型已经具有相同的主键值。这可能发生在   使用“附加”方法或将实体的状态设置为   如果图中的任何实体具有“未更改”或“已修改”   关键值冲突。这可能是因为某些实体是新的,   尚未收到数据库生成的键值。在这种情况下使用   “添加”方法或“已添加”实体状态来跟踪图形和   然后将非新实体的状态设置为“未更改”或“已修改”,如下所示:   合适的。

根据此SO答案:https://stackoverflow.com/a/23228001/1169180我需要使用AsNoTracking(),我不确定如何在AutoMapper中使用它吗?

有什么建议吗?

1 个答案:

答案 0 :(得分:0)

AsNoTracking指的是何时由上下文而不是由Automapper加载实体。之所以会收到错误,是因为在DbContext生命的某个时刻,它已使用该ID加载了实体并正在对其进行跟踪。他们推荐的选项是切换您的实体加载以使用AsNoTracking,它可以有效地告诉EF not 在读取实体时对其进行跟踪。

该问题的另一种解决方案是先检查DbContext的本地缓存中是否存在该实体,如果找到该实体,则使用AutoMapper将属性更改映射到该现有实体,而不是创建一个新实体。 / p>

例如:

var existingEntity = _context.REQUESTEXTs.Local.SingleOrDefault(x => x.EntityId == reqDTO.EntityId);
if(existingEntity != null)
    mapper.Map(reqDto, existingEntity);
else
{
    var entityObj = _mapper.Map<REQUESTEXT>(reqDTO);    
    _context.REQUESTEXTs.Attach(entityObj);
    _context.Entry(entityObj).Property(x => x.CUSTOPTIONCD).IsModified = true;
} 
_context.SaveChanges();

这将检查现有实体的本地缓存(不命中数据库),如果找到,它将使用AutoMapper更新其属性。实体跟踪将记录这些更改,因此,在调用SaveChanges时,修改将进入数据库。如果本地缓存中没有该实体,则我们将创建一个新实例,将其附加,将其标记为已修改,然后保存。

您的示例中似乎缺少一个建议:您应该验证以下假设:

  • 在尝试执行此操作之前,DTO中的ID实际上确实存在于数据库中 和
  • 正在修改的记录可以并且应该由发出此请求的用户编辑。 和
  • 正在更新的数据已完全验证。

如果这是Web应用程序/带有可访问的Controller动作或Web API端点,则可以利用此漏洞允许用户编辑原本不应该的记录,或者以不应该的方式更新记录。 (不要信任客户请求。)每个请求都应经过彻底验证,并且发现的任何偏差都应终止客户会话。