繁体   English   中英

EF Core:无法跟踪实体类型的实例,并且无法注入上下文依赖项

[英]EF Core: The instance of entity type cannot be tracked and context dependency injection

我正在开发一个应该管理团队建设的应用程序,并且我在后端使用.NET Core和EF Core,以及用于依赖项注入的Autofac。 在我的页面中,从后端将所有团队建设放在列表中,然后尝试修改其中一个的值后,出现以下错误:

无法跟踪实体类型“ TeamBuilding”的实例,因为已经跟踪了具有相同键值的{'Id'}的另一个实例。 附加现有实体时,请确保仅附加一个具有给定键值的实体实例。 考虑使用'DbContextOptionsBuilder.EnableSensitiveDataLogging'来查看冲突的键值

这是我使用的类和方法:

控制者

    [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();
        }
    }

依赖注入部分

        var builder = new ContainerBuilder();
        builder.Populate(services);

        builder.RegisterType<UserController>();
        builder.RegisterType<TeamBuildingController>();
        builder.RegisterType<UserService>().As<IUserService>();
        builder.RegisterType<TeamBuildingService>().As<ITeamBuildingService>();
        builder.RegisterType<TeamBuildingContext>().As<DbContext>().InstancePerLifetimeScope();

        builder.RegisterGeneric(typeof(GenericRepository<>))
            .As(typeof(IGenericRepository<>));

        this.ApplicationContainer = builder.Build();

        // Create the IServiceProvider based on the container.
        return new AutofacServiceProvider(this.ApplicationContainer);

为了更精确地详细说明问题,我执行以下操作:

  • 发出GET请求以获取所有团队建设

  • 在相同的浏览器,服务器实例上以及之后,我尝试通过发出PUT请求来修改随机团队建设中的某些字段

  • 我收到上面显示的错误

我知道解决方案之一是先从数据库中获取要更新的对象,然后在该对象上使用新值修改其字段,然后将其传递给update函数。

但是,根据我的代码,请求不应该创建一个新的上下文,然后在完成请求并将响应提供给客户端后,再处理该上下文;对于新请求,则不提供任何信息的全新上下文关于前一个被创建? 就像我现在看到的那样,我使用GET请求创建一个上下文,然后该上下文被PUT请求重用,因此出现“无法跟踪”错误。

我在做什么错,如果一切都很好,那么在Id之后获取对象的方法是一种好习惯吗?

编辑:我只是注意到您的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的更改跟踪有关。

问题是基于问题描述中倒数第二段代码的以下行:

builder.RegisterType<TeamBuildingContext>().As<DbContext>().InstancePerLifetimeScope();

InstancePerLifetimeScope本质上使上下文成为Singleton。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM