我正在开发一个应管理团队建设的应用程序,并且我在后端使用.NET Core和EF Core,以及用于依赖项注入的Autofac。在我的页面中,当我从后端将所有团队建设放在列表中,然后尝试修改其中一个的值时,出现以下错误:
无法跟踪实体类型'TeamBuilding'的实例,因为已经跟踪了另一个具有相同键值的{'Id'}实例。附加现有实体时,请确保仅附加一个具有给定键值的实体实例。考虑使用'DbContextOptionsBuilder.EnableSensitiveDataLogging'查看冲突的键值
这是我使用的类和方法:
控制器
[SerializeField]
private DeathUI deathUi;
// ...
Debug.Log("death");
deathUi.deathUI();
服务
[Produces("application/json")]
[Route("api/teamBuildings")]
public class TeamBuildingController : Controller
{
public ITeamBuildingService _service;
public TeamBuildingController(ITeamBuildingService serviceTeam)
{
_service = serviceTeam;
}
[HttpPost]
public IActionResult Create([FromBody]TeamBuildingForCreationDto teamBuilding)
{
try
{
var existingTb = _service.GetByID(teamBuilding.Id);
if (existingTb != null)
{
return BadRequest("An entry with this id already exists");
}
_service.Create(teamBuilding);
return Ok();
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
[HttpGet]
public IActionResult GetAll()
{
var teamBuildings = _service.GetAll();
if (teamBuildings == null)
{
return NotFound("There are no team buidings");
}
return Ok(teamBuildings);
}
[HttpGet("{id}")]
public IActionResult GetTeambuilding(int id)
{
var teamBuilding = _service.GetByID(id);
if (teamBuilding == null)
{
return NotFound("There is no team buiding with such an ID");
}
return Ok(teamBuilding);
}
[HttpPut]
public IActionResult UpdateTeamBuilding([FromBody]TeamBuildingViewModel viewModel)
{
try
{
var existingTeamBuilding = _service.GetByID(viewModel.Id);
if (existingTeamBuilding == null)
{
return NotFound("There is no team buiding with such an ID");
}
_service.UpdateTeamBuilding(viewModel);
return Ok();
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
}
存储库
public class TeamBuildingService : ITeamBuildingService
{
private IGenericRepository<DAL.Models.TeamBuilding> _repositoryTeam;
public TeamBuildingService(IGenericRepository<DAL.Models.TeamBuilding> repositoryTeam)
{
_repositoryTeam = repositoryTeam;
}
public TeamBuildingDetailsViewModel GetByID(int id)
{
var teamBuilding = _repositoryTeam.GetByID(id);
var viewModel = Mapper.Map<TeamBuildingDetailsViewModel>(teamBuilding);
return viewModel;
}
public IEnumerable<TeamBuildingViewModel> GetAll()
{
//code which returns all the teambuilding from the database, omitted on purpose
}
public TeamBuildingViewModel UpdateTeamBuilding(TeamBuildingViewModel teamBuildingViewModel)
{
var teamBuilding = Mapper.Map<DAL.Models.TeamBuilding>(teamBuildingViewModel);
_repositoryTeam.Edit(teamBuilding);
_repositoryTeam.Commit();
return teamBuildingViewModel;
}
}
}
依赖注入部分
public class GenericRepository<T> : IGenericRepository<T> where T : class
{
public DbContext _context;
public DbSet<T> dbset;
public GenericRepository(DbContext context)
{
_context = context;
dbset = context.Set<T>();
}
public IQueryable<T> GetAll()
{
return dbset;
}
public T GetByID(params object[] keyValues)
{
return dbset.Find(keyValues);
}
public void Edit(T entity)
{
_context.Entry(entity).State = EntityState.Modified;
}
public void Insert(T entity)
{
dbset.Add(entity);
}
public void Delete(T entity)
{
_context.Entry(entity).State = EntityState.Deleted;
}
public T GetByFunc(Func<T, bool> func)
{
return dbset.AsQueryable().Where(x => func(x)).FirstOrDefault();
}
public void Commit()
{
_context.SaveChanges();
}
}
为更准确地详细说明问题,我做了以下事情:
发出GET请求以获取所有团队建设
在相同的浏览器,服务器实例上以及之后,我尝试通过发出PUT请求来修改随机团队建设中的某些字段
我收到上面显示的错误
我知道解决方案之一是首先从数据库中获取要更新的对象,然后在该对象上使用新值修改其字段,然后将其传递给update函数。
但是根据我的代码,请求不应该创建一个新的上下文,然后在完成请求并将响应提供给客户端之后,该上下文将被处置;对于新请求,一个全新的上下文没有有关上一个的信息被创建吗?就像我现在看到的那样,我使用GET请求创建一个上下文,然后该上下文被PUT请求重用,因此出现“无法跟踪”错误。
我在做什么错了,如果实际上一切正常,那么在Id之后获取对象的方法是一种好习惯吗?
答案 0 :(得分:1)
编辑:我刚刚注意到您的GetById方法返回了一个viewmodel。您必须像这样操纵实体
var teamBuilding = _repositoryTeam.GetByID(id);
Mapper.Map(teamBuildingViewModel, teamBuilding);
_repositoryTeam.Edit(teamBuilding);
_repositoryTeam.Commit();
这是这行
var teamBuilding = Mapper.Map<DAL.Models.TeamBuilding>(teamBuildingViewModel);
这将创建对象Teambuilding的新实例。您需要像在控制器中一样加载现有的(无论如何都不应在其中完成)。在您的服务级别上这样做:
var teamBuilding = this.GetByID(viewModel.Id);
Mapper.Map(teamBuildingViewModel, teamBuilding);
_repositoryTeam.Edit(teamBuilding);
_repositoryTeam.Commit();
现在,由dbcontext跟踪的对象是相同的,并且更新可以正常工作。现在,您将尝试在数据库中创建新行。这与ef-core的更改跟踪有关。
答案 1 :(得分:-1)
问题是代码中的以下行基于问题描述的倒数第二段:
builder.RegisterType<TeamBuildingContext>().As<DbContext>().InstancePerLifetimeScope();
InstancePerLifetimeScope
本质上使上下文成为Singleton。