The instance of entity type cannot be tracked because another instance of this type with the same key is already being tracked

前端 未结 10 1131
一向
一向 2020-11-29 20:11

I have a Service Object Update

public bool Update(object original, object modified)
{
    var originalClient = (Client)original;
    var modifi         


        
相关标签:
10条回答
  • 2020-11-29 20:41

    I had the same issue (EF Core) while setting up xUnit tests. What 'fixed' it for me in testing was looping through the change tracker entities after setting up the seed data.

    • at the bottom of the SeedAppDbContext() method.

    I set up a Test Mock Context:

    /// <summary>
    /// Get an In memory version of the app db context with some seeded data
    /// </summary>
    public static AppDbContext GetAppDbContext(string dbName)
    {
        //set up the options to use for this dbcontext
        var options = new DbContextOptionsBuilder<AppDbContext>()
            .UseInMemoryDatabase(databaseName: dbName)
            //.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking)
            .Options;
    
        var dbContext = new AppDbContext(options);
        dbContext.SeedAppDbContext();
        return dbContext;
    }
    

    Extension method to add some seed data:

    • and detach entities in foreach loop at bottom of method.
        public static void SeedAppDbContext(this AppDbContext appDbContext)
        {
           // add companies
           var c1 = new Company() { Id = 1, CompanyName = "Fake Company One", ContactPersonName = "Contact one", eMail = "one@caomp1.com", Phone = "0123456789", AdminUserId = "" };
           c1.Address = new Address() { Id = 1, AddressL1 = "Field Farm", AddressL2 = "Some Lane", City = "some city", PostalCode = "AB12 3CD" };
           appDbContext.CompanyRecords.Add(c1);
                            
           var nc1 = new Company() { Id = 2, CompanyName = "Test Company 2", ContactPersonName = "Contact two", eMail = "two@comp2.com", Phone = "0123456789", Address = new Address() { }, AdminUserId = "" };
           nc1.Address = new Address() { Id = 2, AddressL1 = "The Barn", AddressL2 = "Some Lane", City = "some city", PostalCode = "AB12 3CD" };
           appDbContext.CompanyRecords.Add(nc1);
    
           //....and so on....
                
           //last call to commit everything to the memory db
           appDbContext.SaveChanges();
    
           //and then to detach everything 
           foreach (var entity in appDbContext.ChangeTracker.Entries())
           {
               entity.State = EntityState.Detached;
           }
        }
    

    The controller put method

    The .ConvertTo<>() Method is an extension method from ServiceStack

     [HttpPut]
    public async Task<IActionResult> PutUpdateCompany(CompanyFullDto company)
    {
        if (0 == company.Id)
            return BadRequest();
        try
        {
            Company editEntity = company.ConvertTo<Company>();
            
            //Prior to detaching an error thrown on line below (another instance with id)
            var trackedEntity = _appDbContext.CompanyRecords.Update(editEntity);
            
    
            await _appDbContext.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException dbError)
        {
            if (!CompanyExists(company.Id))
                return NotFound();
            else
                return BadRequest(dbError);
        }
        catch (Exception Error)
        {
            return BadRequest(Error);
        }
        return Ok();
    }
    

    and the test:

        [Fact]
        public async Task PassWhenEditingCompany()
        {
            var _appDbContext = AppDbContextMocker.GetAppDbContext(nameof(CompaniesController));
            var _controller = new CompaniesController(null, _appDbContext);
    
            //Arrange
            const string companyName = "Fake Company One";
            const string contactPerson = "Contact one";
    
            const string newCompanyName = "New Fake Company One";
            const string newContactPersonName = "New Contact Person";
    
            //Act
            var getResult = _controller.GetCompanyById(1);
            var getEntity = (getResult.Result.Result as OkObjectResult).Value;
            var entityDto = getEntity as CompanyFullDto;
    
    
            //Assert
            Assert.Equal(companyName, entityDto.CompanyName);
            Assert.Equal(contactPerson, entityDto.ContactPersonName);
            Assert.Equal(1, entityDto.Id);
    
            //Arrange
            Company entity = entityDto.ConvertTo<Company>();
            entity.CompanyName = newCompanyName;
            entity.ContactPersonName = newContactPersonName;
            CompanyFullDto entityDtoUpd = entity.ConvertTo<CompanyFullDto>();
    
            //Act
            var result = await _controller.PutUpdateCompany(entityDtoUpd) as StatusCodeResult;
    
            //Assert           
            Assert.True(result.StatusCode == 200);
    
            //Act
            getResult = _controller.GetCompanyById(1);
            getEntity = (getResult.Result.Result as OkObjectResult).Value;
            
            entityDto = getEntity as CompanyFullDto;
            
            //Assert
            Assert.Equal(1, entityDto.Id); // didn't add a new record
            Assert.Equal(newCompanyName, entityDto.CompanyName); //updated the name
            Assert.Equal(newContactPersonName, entityDto.ContactPersonName); //updated the contact
    
    //make sure to dispose of the _appDbContext otherwise running the full test will fail.
    _appDbContext.Dispose();
        }
    
    0 讨论(0)
  • 2020-11-29 20:44

    In my case, the table's id column was not set as an Identity column.

    0 讨论(0)
  • 2020-11-29 20:51
    public async Task<Product> GetValue(int id)
        {
            Product Products = await _context.Products.AsNoTracking().FirstOrDefaultAsync(x => x.Id == id);
            return Products;
        }
    

    AsNoTracking()

    0 讨论(0)
  • 2020-11-29 20:55
    public static void DetachEntity<T>(this DbContext dbContext, T entity, string propertyName) where T: class, new()
    {
       try
       {
          var dbEntity = dbContext.Find<T>(entity.GetProperty(propertyName));
          if (dbEntity != null)
              dbContext.Entry(dbEntity).State = EntityState.Detached;
          dbContext.Entry(entity).State = EntityState.Modified;
       }
       catch (Exception)
       {
            throw;
       }
    }
    
    
     public static object GetProperty<T>(this T entity, string propertyName) where T : class, new()
     {
        try
        {
            Type type = entity.GetType();
            PropertyInfo propertyInfo = type.GetProperty(propertyName);
            object value = propertyInfo.GetValue(entity);
            return value;
        }
        catch (Exception)
        {
             throw;
        }
     }
    

    I made this 2 extension methods, this is working really well.

    0 讨论(0)
  • 2020-11-29 20:55

    If your data has changed every once,you will notice dont tracing the table.for example some table update id ([key]) using tigger.If you tracing ,you will get same id and get the issue.

    0 讨论(0)
  • 2020-11-29 20:57

    I got this error from my background service. I solved which creating a new scope.

                    using (var scope = serviceProvider.CreateScope())
                    {
                          // Process
                    }
    
    0 讨论(0)
提交回复
热议问题