简体   繁体   English

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

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

I am developing an app that should manage teambuildings, and I am using .NET Core and EF Core for my backend, together with Autofac for dependency injection. 我正在开发一个应该管理团队建设的应用程序,并且我在后端使用.NET Core和EF Core,以及用于依赖项注入的Autofac。 In my page, after I get all my teambuildings in a list from the backend, and then I try to modify the values for one of them, I get the following error: 在我的页面中,从后端将所有团队建设放在列表中,然后尝试修改其中一个的值后,出现以下错误:

The instance of entity type 'TeamBuilding' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked. 无法跟踪实体类型“ TeamBuilding”的实例,因为已经跟踪了具有相同键值的{'Id'}的另一个实例。 When attaching existing entities, ensure that only one entity instance with a given key value is attached. 附加现有实体时,请确保仅附加一个具有给定键值的实体实例。 Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values 考虑使用'DbContextOptionsBuilder.EnableSensitiveDataLogging'来查看冲突的键值

Here are the classes and methods I use: 这是我使用的类和方法:

Controller 控制者

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

Service 服务

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;
        }
    }
}

Repository 资料库

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

The Dependency Injection part 依赖注入部分

        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);

To detail the problem more exactly, I do the following things: 为了更精确地详细说明问题,我执行以下操作:

  • make a GET request to get all the teambuildings 发出GET请求以获取所有团队建设

  • from the same browser, server instance and immediately after, I try to modify some of the fields on a random teambuilding by making a PUT request 在相同的浏览器,服务器实例上以及之后,我尝试通过发出PUT请求来修改随机团队建设中的某些字段

  • I get the error shown above 我收到上面显示的错误

I know one of the solutions is to get the object I want to update from the database first , then on that object to modify its fields with the new values, then pass it to the update function. 我知道解决方案之一是先从数据库中获取要更新的对象,然后在该对象上使用新值修改其字段,然后将其传递给update函数。

But shouldn't the request, according to my code, create a new context, then after the request is done and the response was given to the client the context to be disposed, and for a new request a completely new context that has no information about the previous one be created? 但是,根据我的代码,请求不应该创建一个新的上下文,然后在完成请求并将响应提供给客户端后,再处理该上下文;对于新请求,则不提供任何信息的全新上下文关于前一个被创建? As I see it now, I create a context with the GET request, then the context is reused by the PUT request, hence the "Cannot be tracked" error. 就像我现在看到的那样,我使用GET请求创建一个上下文,然后该上下文被PUT请求重用,因此出现“无法跟踪”错误。

What am I doing wrong, and if everything is actually ok, is the method of getting the object after the Id first the good practice? 我在做什么错,如果一切都很好,那么在Id之后获取对象的方法是一种好习惯吗?

Edit: I just noticed your GetById method returns a viewmodel. 编辑:我只是注意到您的GetById方法返回一个viewmodel。 You must manipulate the entity like that 您必须像这样操纵实体

var teamBuilding = _repositoryTeam.GetByID(id);
Mapper.Map(teamBuildingViewModel, teamBuilding);
_repositoryTeam.Edit(teamBuilding);
_repositoryTeam.Commit();

It is this line here 这是这条线

var teamBuilding = Mapper.Map<DAL.Models.TeamBuilding>(teamBuildingViewModel);

This creates a new instance of the object Teambuilding. 这将创建对象Teambuilding的新实例。 You need to load the existing one as you do in the controller (which should not be done there anyway). 您需要像在控制器中一样加载现有的(无论如何都不应在其中完成)。 Do it like that from your service-class: 在您的服务级别上这样做:

var teamBuilding = this.GetByID(viewModel.Id);
Mapper.Map(teamBuildingViewModel, teamBuilding);
_repositoryTeam.Edit(teamBuilding);
_repositoryTeam.Commit();

Now the object that is being tracked by the dbcontext is the same and update will work just fine. 现在,由dbcontext跟踪的对象是相同的,并且更新将正常工作。 The way you are doing it now it would try to create a new row in the database. 现在,您将尝试在数据库中创建新行。 This is related to the change-tracking of ef-core. 这与ef-core的更改跟踪有关。

The problem is the following line in your code based on the second to last paragraph in your problem description: 问题是基于问题描述中倒数第二段代码的以下行:

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

The InstancePerLifetimeScope essentially makes the context a Singleton. InstancePerLifetimeScope本质上使上下文成为Singleton。

暂无
暂无

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

相关问题 InvalidOperationException:无法跟踪实体实例 - EF Core - InvalidOperationException: The instance of entity cannot be tracked - EF Core 无法跟踪实体类型的实例,因为另一个实例...更新数据库时出现 EF Core 错误 - The instance of entity type cannot be tracked because another instance… EF Core error when updating db EF Core:无法跟踪实体类型的实例,因为另一个实例具有相同的键值 - EF Core: The instance of entity type cannot be tracked because another instance with the same key value EF Core(内存数据库)-'无法跟踪实体类型 X 的实例,因为另一个具有键值的实例 - EF Core (in-memory database) - 'The instance of entity type X cannot be tracked because another instance with the key value EF:无法跟踪实体类型X的实例,因为已经跟踪了具有相同密钥的此类型的另一个实例 - EF: The instance of entity type X cannot be tracked because another instance of this type with the same key is already being tracked 带有 Automapper 的 EF Core 抛出异常“无法跟踪实体类型” - EF Core with Automapper throw Exception 'Entity type cannot be tracked' 实体框架核心 - 无法跟踪实体类型的实例,因为已在跟踪具有键值的另一个实例 - Entity framework Core - The instance of entity type cannot be tracked because another instance with the key value is already being tracked 无法跟踪实体类型的实例 - The instance of entity type cannot be tracked 实体类型“”的实例无法跟踪 - The instance of entity type '' cannot be tracked 实体类型的实体框架实例无法跟踪 - Entity Framework instance of entity type cannot be tracked
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM