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

C#asp.netEntity Framework

C# Problem Overview


I have a Service Object Update

public bool Update(object original, object modified)
{
    var originalClient = (Client)original;
    var modifiedClient = (Client)modified;
    _context.Clients.Update(originalClient); //<-- throws the error
    _context.SaveChanges();
    //Variance checking and logging of changes between the modified and original
}

This is where I am calling this method from:

public IActionResult Update(DetailViewModel vm)
{
    var originalClient = (Client)_service.GetAsNoTracking(vm.ClientId);
    var modifiedClient = (Client)_service.Fetch(vm.ClientId.ToString());
    // Changing the modifiedClient here
    _service.Update(originalClient, modifiedClient);
}

Here is the GetAsNotTracking method:

public Client GetAsNoTracking(long id)
{
    return GetClientQueryableObject(id).AsNoTracking().FirstOrDefault();
}

Fetch method:

public object Fetch(string id)
{
   long fetchId;
   long.TryParse(id, out fetchId);
   return GetClientQueryableObject(fetchId).FirstOrDefault();
}

GetClientQueryableObject:

private Microsoft.Data.Entity.Query.IIncludableQueryable<Client, ActivityType> GetClientQueryableObject(long searchId)
{
    return _context.Clients
        .Where(x => x.Id == searchId)
        .Include(x => x.Opportunities)
        .ThenInclude(x => x.BusinessUnit)
        .Include(x => x.Opportunities)
        .ThenInclude(x => x.Probability)
        .Include(x => x.Industry)
        .Include(x => x.Activities)
        .ThenInclude(x => x.User)
        .Include(x => x.Activities)
        .ThenInclude(x => x.ActivityType);
 }

Any ideas?

I have looked the following articles / discussions. To no avail:ASP.NET GitHub Issue 3839

UPDATE:

Here are the changes to GetAsNoTracking:

public Client GetAsNoTracking(long id)
{
    return GetClientQueryableObjectAsNoTracking(id).FirstOrDefault();
}

GetClientQueryableObjectAsNoTracking:

private IQueryable<Client> GetClientQueryableObjectAsNoTracking(long searchId)
{
    return _context.Clients
        .Where(x => x.Id == searchId)
        .Include(x => x.Opportunities)
        .ThenInclude(x => x.BusinessUnit)
        .AsNoTracking()
        .Include(x => x.Opportunities)
        .ThenInclude(x => x.Probability)
        .AsNoTracking()
        .Include(x => x.Industry)
        .AsNoTracking()
        .Include(x => x.Activities)
        .ThenInclude(x => x.User)
        .AsNoTracking()
        .Include(x => x.Activities)
        .ThenInclude(x => x.ActivityType)
        .AsNoTracking();
}

C# Solutions


Solution 1 - C#

Without overriding EF track system, you can also Detach the 'local' entry and attach your updated entry before saving :

// 
var local = _context.Set<YourEntity>()
    .Local
    .FirstOrDefault(entry => entry.Id.Equals(entryId));

// check if local is not null 
if (local != null)
{
    // detach
    _context.Entry(local).State = EntityState.Detached;
}
// set Modified flag in your entry
_context.Entry(entryToUpdate).State = EntityState.Modified;

// save 
_context.SaveChanges();

UPDATE: To avoid code redundancy, you can do an extension method :

public static void DetachLocal<T>(this DbContext context, T t, string entryId) 
	where T : class, IIdentifier 
{
	var local = context.Set<T>()
		.Local
		.FirstOrDefault(entry => entry.Id.Equals(entryId));
	if (!local.IsNull())
	{
		context.Entry(local).State = EntityState.Detached;
	}
	context.Entry(t).State = EntityState.Modified;
}

My IIdentifier interface has just an Id string property.

Whatever your Entity, you can use this method on your context :

_context.DetachLocal(tmodel, id);
_context.SaveChanges();

Solution 2 - C#

public async Task<Product> GetValue(int id)
    {
        Product Products = await _context.Products.AsNoTracking().FirstOrDefaultAsync(x => x.Id == id);
        return Products;
    }

AsNoTracking()

Solution 3 - C#

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

Solution 4 - C#

It sounds as you really just want to track the changes made to the model, not to actually keep an untracked model in memory. May I suggest an alternative approach which will remove the problem entirely?

EF will automticallly track changes for you. How about making use of that built in logic?

Ovverride SaveChanges() in your DbContext.

    public override int SaveChanges()
    {
        foreach (var entry in ChangeTracker.Entries<Client>())
        {
            if (entry.State == EntityState.Modified)
            {
                // Get the changed values.
                var modifiedProps = ObjectStateManager.GetObjectStateEntry(entry.EntityKey).GetModifiedProperties();
                var currentValues = ObjectStateManager.GetObjectStateEntry(entry.EntityKey).CurrentValues;
                foreach (var propName in modifiedProps)
                {
                    var newValue = currentValues[propName];
                    //log changes
                }
            }
        }

        return base.SaveChanges();
    }

Good examples can be found here:

https://stackoverflow.com/questions/26355486/entity-framework-6-audit-track-changes

https://stackoverflow.com/questions/6867459/implementing-audit-log-change-history-with-mvc-entity-framework

EDIT: Client can easily be changed to an interface. Let's say ITrackableEntity. This way you can centralize the logic and automatically log all changes to all entities that implement a specific interface. The interface itself doesn't have any specific properties.

    public override int SaveChanges()
    {
        foreach (var entry in ChangeTracker.Entries<ITrackableClient>())
        {
            if (entry.State == EntityState.Modified)
            {
                // Same code as example above.
            }
        }

        return base.SaveChanges();
    }

Also, take a look at eranga's great suggestion to subscribe instead of actually overriding SaveChanges().

Solution 5 - C#

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 = "[email protected]", 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 = "[email protected]", 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();
    }

Solution 6 - C#

For me this just fixed the problem. Add this code before any update

_context.ChangeTracker.Clear()

From Microsoft Documentation

> Stops tracking all currently tracked entities. > > DbContext is designed to have a short lifetime where a new instance is > created for each unit-of-work. This manner means all tracked entities > are discarded when the context is disposed at the end of each > unit-of-work. However, clearing all tracked entities using this method > may be useful in situations where creating a new context instance is > not practical. > > This method should always be preferred over detaching every tracked > entity. Detaching entities is a slow process that may have side > effects. This method is much more efficient at clearing all tracked > entities from the context. > > Note that this method does not generate StateChanged events since > entities are not individually detached.

Solution 7 - C#

You could just set the entity to detatched after saving, like this:

public async Task<T> Update(int id, T entity)
    {
        entity.Id = id;
        _ctx.Set<T>().Update(entity);
        await _ctx.SaveChangesAsync();
        _ctx.Entry(entity).State = EntityState.Detached; //detach saved entity
        return entity;
    }

Solution 8 - C#

In EF core - also make sure that you dont set both the foreign key and the foreign key navigation property. I got this error when I set both the key and the property.

e.g.

        new VerificationAccount()
        {
            Account = konto_1630,
            VerificationRowType = VerificationRowType.Template,
            // REMOVED THE LINE BELOW AND THE ERROR WENT AWAY
            //VerificationAccount = verificationAccounts.First(x => x.Account == konto_1630),
            VerificationId = verificationId
        }

Solution 9 - C#

For me, I was experiencing this issue while also using AutoMapper and .NET 6. To resolve it, I changed the code from:

DbItem? result = await _dbContext.DbItems.FirstOrDefaultAsync(t => t.Id == id);
if (result == null)
{
    return null;
}
DbItem mappedItem = _mapper.Map<DbItem>(dto);  //problematic line
var updatedItem = _dbContext.DbItems.Update(mappedItem);

To:

DbItem? result = await _dbContext.DbItems.FirstOrDefaultAsync(t => t.Id == id);
if (result == null)
{
    return null;
}
_mapper.Map(dto, result);  //the fix
var updatedItem = _dbContext.DbItems.Update(result);

The problematic line created a NEW DbItem with the same key value(s), leading to the issue. The fix line maps the fields from the DTO to the original DbItem.

Solution 10 - C#

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

                using (var scope = serviceProvider.CreateScope())
                {
                      // Process
                }

Solution 11 - C#

Arhhh this got me and I spent a lot of time troubleshooting it. The problem was my tests were being executed in Parellel (the default with XUnit).

To make my test run sequentially I decorated each class with this attribute:

[Collection("Sequential")]

This is how I worked it out: https://stackoverflow.com/questions/1408175/execute-unit-tests-serially-rather-than-in-parallel/34876963


I mock up my EF In Memory context with GenFu:

private void CreateTestData(TheContext dbContext)
{
	GenFu.GenFu.Configure<Employee>()
	   .Fill(q => q.EmployeeId, 3);
	var employee = GenFu.GenFu.ListOf<Employee>(1);

	var id = 1;
	GenFu.GenFu.Configure<Team>()
		.Fill(p => p.TeamId, () => id++).Fill(q => q.CreatedById, 3).Fill(q => q.ModifiedById, 3);
	var Teams = GenFu.GenFu.ListOf<Team>(20);
	dbContext.Team.AddRange(Teams);

	dbContext.SaveChanges();
}

When Creating Test Data, from what I can deduct, it was alive in two scopes (once in the Employee's Tests while the Team tests were running):

public void Team_Index_should_return_valid_model()
{
	using (var context = new TheContext(CreateNewContextOptions()))
	{
		//Arrange
		CreateTestData(context);
		var controller = new TeamController(context);

		//Act
		var actionResult = controller.Index();

		//Assert
		Assert.NotNull(actionResult);
		Assert.True(actionResult.Result is ViewResult);
		var model = ModelFromActionResult<List<Team>>((ActionResult)actionResult.Result);
		Assert.Equal(20, model.Count);
	}
}

Wrapping both Test Classes with this sequential collection attribute has cleared the apparent conflict.

[Collection("Sequential")]

Additional references:

https://github.com/aspnet/EntityFrameworkCore/issues/7340
https://stackoverflow.com/questions/50530949/ef-core-2-1-in-memory-db-not-updating-records
http://www.jerriepelser.com/blog/unit-testing-aspnet5-entityframework7-inmemory-database/
http://gunnarpeipman.com/2017/04/aspnet-core-ef-inmemory/
https://github.com/aspnet/EntityFrameworkCore/issues/12459
https://stackoverflow.com/questions/42138960/preventing-tracking-issues-when-using-ef-core-sqllite-in-unit-tests

Solution 12 - C#

I faced the same problem but the issue was very silly, By mistake I have given wrong relationship I have given relationship between 2 Ids.

Solution 13 - C#

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.

Solution 14 - C#

Cant update the DB row. I was facing the same error. Now working with following code:

_context.Entry(_SendGridSetting).CurrentValues.SetValues(vm);
await _context.SaveChangesAsync();

Solution 15 - C#

This error message can happen if you have duplicate entries/entities and run SaveChanges().

Solution 16 - C#

If you have setted two or more tables with 'Id' or columns name with the same column name, the easiest way is to change OnModelCreating method in the context class.

In this case I have to change the 'Id' to 'AbandonedCartId' and tell to entity that object has column name 'Id'

entity.Property(e => e.AbandonedCartId).HasColumnName("Id");

Example

public partial class AbandonedCart
    {
        public int AbandonedCartId { get; set; }
        public double? CheckoutId { get; set; }
        public int? AppId { get; set; }
        public double? CustomerId { get; set; }
        
    }
protected override void OnModelCreating(ModelBuilder modelBuilder)
 {
            modelBuilder.Entity<AbandonedCart>(entity =>
            {
                entity.Property(e => e.AbandonedCartId).HasColumnName("Id");
                entity.Property(e => e.CreatedAt).HasColumnType("datetime");

                entity.HasOne(d => d.App)
                    .WithMany(p => p.AbandonedCart)
                    .HasForeignKey(d => d.AppId)
                    .HasConstraintName("FK_AbandonedCart_Apps");
            });
}

Solution 17 - C#

I have had the same problem before and solved it by replacing AddDbContext with the AddDbContextFactory to the services container.

This is how I solved the issue:

Instead of registering AddDbContext, I registered AddDbContextFactory instead, like this:

public void ConfigureServices(IServiceCollection services)
{
services.AddDbContextFactory<YourApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("YourDatabaseConnectionString")));
}

IMPORTANT REMINDER:

Do not register AddDbContext and AddDbContextFactory together because you will get a System.AggregateException: 'Some services are not able to be constructed...' exception. Use AddDbContextFactory instead.

Your ApplicationDbContext class must expose a public constructor with a DbContextOptions<YourApplicationDbContext> parameter like this:

public class YourApplicationDbContext : DbContext
{
    public ApplicationDbContext(DbContextOptions<YourApplicationDbContext> options): base(options){}
}

The DbContextFactory factory can then be used through constructor injection like this:

private readonly IDbContextFactory<YourApplicationDbContext> dbContextFactory;

public YourConstructor(IDbContextFactory<YourApplicationDbContext> dbContextFactory)
{
    dbContextFactory = dbContextFactory;
}

or

public YourController(IDbContextFactory<YourApplicationDbContext> dbContextFactory)
{
    dbContextFactory = dbContextFactory;
}

The injected factory can then be used to construct DbContext instances in the service code like this:

   using (var context = dbContextFactory.CreateDbContext())
        {
            // your database CRUD code comes in here... for example:
            context.DatabaseTable.Update(suppliedModel);
            await context.SaveChangesAsync();
        }

When you may consider this option: Registering a factory instead of registering the context type directly allows you easy creation of new DbContext instances. It is also recommended for Blazor applications.

I hope this helps someone facing this issue. Cheers!

Solution 18 - C#

I had this problem myself. Entity Framework keeps track of every object you insert into the database. So when you insert a duplicate record of the same object with a few fields being changed, EF will throw this error. I got around it by deep cloning the object I'm trying to re-insert, and it went through.

    public static T DeepClone<T>(this T a)
    {
        using (MemoryStream stream = new MemoryStream())
        {
            BinaryFormatter formatter = new BinaryFormatter();
            formatter.Serialize(stream, a);
            stream.Position = 0;
            return (T)formatter.Deserialize(stream);
        }
    }

then:

var cloned = objectYouAreTryingToReinsert.deepClone();
context.objects.add(cloned);
await context.SaveChangesAsync();

Solution 19 - C#

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionRijnhardtView Question on Stackoverflow
Solution 1 - C#Andres TalaveraView Answer on Stackoverflow
Solution 2 - C#SHUBHASIS MAHATAView Answer on Stackoverflow
Solution 3 - C#daviesdoesitView Answer on Stackoverflow
Solution 4 - C#smoksnesView Answer on Stackoverflow
Solution 5 - C#CobyCView Answer on Stackoverflow
Solution 6 - C#sabsabView Answer on Stackoverflow
Solution 7 - C#FrancescoView Answer on Stackoverflow
Solution 8 - C#SgeddaView Answer on Stackoverflow
Solution 9 - C#Jonathan B.View Answer on Stackoverflow
Solution 10 - C#Recep DumanView Answer on Stackoverflow
Solution 11 - C#Jeremy ThompsonView Answer on Stackoverflow
Solution 12 - C#Manthan DevaniView Answer on Stackoverflow
Solution 13 - C#Randy Peña JimenezView Answer on Stackoverflow
Solution 14 - C#R M Shahidul Islam ShahedView Answer on Stackoverflow
Solution 15 - C#johnbtView Answer on Stackoverflow
Solution 16 - C#Francisco FernandezView Answer on Stackoverflow
Solution 17 - C#Russell ChidhakwaView Answer on Stackoverflow
Solution 18 - C#IrshuView Answer on Stackoverflow
Solution 19 - C#menxinView Answer on Stackoverflow