实体框架跟踪第二次更新后的变化



该应用程序是建立在一堆asp.net core mvc和实体框架。我有一张地图,上面有记号笔。我想通过文本框修改某个对象的参数。来自前端的请求是用axios编写的,它可以完美地工作。从我第一次得到数据库中的变化开始。(mysql, provider:柚.mysql)。当我第一次尝试访问get请求时,我得到的是对象的旧状态。

HttpGet请求描述如下:

public async Task<IEnumerable<Poi>> GetPois()
{
var pois = await _poiService.GetPois();
if (pois.Status == Domain.Enum.StatusCode.Ok)
{
return pois.Data;
}
else { return null; }
}

我有一个接口,它描述了对Poi对象的必要操作集。

IPoiService的描述如下:

public interface IPoiService
{
Task<BaseResponse<IEnumerable<Poi>>> GetPois();
Task<BaseResponse<Poi>> GetPoi();
Task<BaseResponse<bool>> DeletePoi();
Task<BaseResponse<Poi>> CreatePoi();
Task<BaseResponse<Poi>> UpdatePoi(Poi entity);
}

处理Poi对象的服务描述如下:

public async Task<BaseResponse<IEnumerable<Poi>>> GetPois()
{
try
{
return new BaseResponse<IEnumerable<Poi>>
{
Data = await _poiRepository.GetAll().ToListAsync(),
Status = Domain.Enum.StatusCode.Ok
};
}
catch(Exception ex)
{
return new BaseResponse<IEnumerable<Poi>>
{
Status = Domain.Enum.StatusCode.InternalServerError,
Description = $"[GetPois]: {ex.Message}"
};
}
}

baserresponse和相应的接口表示来自数据库的响应,因此它不会以任何方式影响更新问题。我还有一个存储库,它直接实现了数据库级别的实例操作。

存储库描述如下:

public class PoiRepository : IBaseRepository<Poi>
{
private readonly ApplicationDbContext db;
public PoiRepository(ApplicationDbContext db)
{
this.db = db;
db.Database.OpenConnection();
}
public Task Create(Poi entity)
{
throw new NotImplementedException();
}
public Task Delete(Poi entity)
{
throw new NotImplementedException();
}
public IQueryable<Poi> GetAll()
{
return db.Pois;           
}
public Poi Update(Poi entity)
{
db.Pois.Update(entity);
db.SaveChanges();

return entity;
}
}

因此,我得到的问题是,为了获得当前数据,我需要执行两个HttpGet请求,只有在EF Core将返回其当前值给我。

Update(entity)发出警告的原因是您正在服务器和客户端之间传递实体。当控制器返回一个View(entity)时,你正在向视图引擎发送一个引用实体来构建视图。视图的@Model允许您应用绑定,但它不是实体的客户端副本。但是,当您的表单提交或Ajax调用等调用@model,这不是一个实体,更不用说实体视图引擎给出。它将只是数据的一个副本,并且只是视图绑定所能填充的完整的副本。

因此,如果不逐字遍历应用程序,很难推断出您正在目睹的究竟是什么,但我的直觉告诉我,您很可能会对您认为的传递实体引用的内容感到困惑。可以这样考虑,在POST操作中,您可以为模型的每个值接受一组整数、字符串等,或者接受具有与实体相同字段的完全不同的类定义(DTO/ViewModel)。ASP。Net将尝试使用通过Form POST或Ajax调用提交的数据进行填充。通过接受一个"实体"您只是告诉EF将数据填充到一个新的未跟踪实体类中。它与最初加载的DbContext不是同一个实例,并且DbContext与最初加载实体时是不同的实例(或者应该是不同的实例),它没有跟踪最初加载的实体。

结果对象将只包含视图碰巧存储在各个绑定控件中的细节,在幕后拼凑在一起。

我的建议仅仅是永远不要传递实体给视图,尤其是从视图传递实体。使用显式ViewModel来表示发送到视图和从视图发送的状态,然后在Update方法中:

  • 使用ViewModel ID获取实际实体,
  • 检查并发令牌(RowVersionNumber/Timestamp)以确保自您最初获取数据以填充视图以来没有对DB进行更改。(可选,但推荐)
  • 验证视图模型中的数据
  • 将数据从视图模型复制到实体。(Automapper可以在这里提供帮助)
  • SaveChanges()

不使用UpdateAttachDbContext/DbSet.

最新更新