75

I'm using generic repository pattern in asp.net core 2.0 which can not dispose repository object, when I am going to update the entry its updated for one time successfully but when I am trying to update for more then once it throws the following exception:

The instance of entity type 'Company' cannot be tracked because another instance with the same key value for {'ID'} is already being tracked. 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.

public ActionResult Edit(Int64 id, Company collection)
{
    try
    {
        // TODO: Add update logic here
        interfaceobj.updateModel(collection);
        interfaceobj.Save();
        return RedirectToAction(nameof(Index));
    }
    catch(Exception ex)
    {
        throw ex;
    }
}
Rudi Visser
  • 21,350
  • 5
  • 71
  • 97
Ali Raza
  • 981
  • 1
  • 8
  • 17
  • 1
    Read [ask] and create a [mcve]. You shouldn't reuse DbContext between requests. – CodeCaster Jan 11 '18 at 08:47
  • It would be great if you had provided some code instead of saying I've implemented *Repository Pattern*. The problem seems to exist in how you've implemented that! This question is not helping others with the same problem. – Shahryar Saljoughi Jul 28 '20 at 08:24
  • There's nothing in the code that shows what EF does, it's all hidden behind custom code. This can't possibly be answered. What is `interfaceobj`? What is the status of the code? Why the `TODO`? Etc. – Gert Arnold May 04 '21 at 11:22

10 Answers10

76

Your DB Context is being shared by multiple requests, meaning that the entity you're editing has been tracked already.

This is likely because your repository service is a Singleton rather than Scoped, and so your DB Context is being reused with the entity being tracked when it's pulled out, and then put back in to the same instance of the DB Context.

Instead of a Singleton repository, use a Scoped Repository, which means that a new instance will be created for each request. Likewise, you will also have a per-request DB Context.

When you register your service it will be using services.AddSingleton<..>

Change this to services.AddScoped<..>, when you inject it into your controller you will then get a new instance per request and your updates should work fine.

moke
  • 2,611
  • 2
  • 18
  • 16
Rudi Visser
  • 21,350
  • 5
  • 71
  • 97
  • Thanks for your help , it works for me now , you really saved my day – Ali Raza Jan 11 '18 at 09:58
  • 1
    my bad i was registring for singlton service services.AddSingleton, GenericRepository>(); and now i changed it into service.AdScoped and now it works perfectly for me :) – Ali Raza Jan 11 '18 at 10:09
  • @Andre are you initialising another DB Context somewhere? – Rudi Visser Sep 10 '19 at 19:18
  • @RudiVisser No, actually just at startup... I really do not know how to solve that... Maybe should I ask here, but I think people gonna say it is a repeated issue ... – Andre Sep 11 '19 at 10:16
  • 2
    Actually I was using Scoped already, I have solved the problem and seems to be a EF bug... – Andre Sep 28 '19 at 18:40
  • @Andre how did you solve this? My context is added with `.AddDbContext(ConfigDb)` – JonathanPeel Oct 16 '19 at 03:16
  • 3
    @JonathanPeel, on my case it was an EF bug as far I know... so to solve, first of all I made some changes in order to see the query like here https://stackoverflow.com/q/1412863/4482337, after that I saw that EF was doing something very weird, somehow it was trying to put data that was already there and I notice that it was always the same attribute with problem, well at the end I discovered that somehow EF was getting lost because I had the attribute string "name" on many classes, so I just change this attribute to string "foo" and it just worked and never crash again. – Andre Oct 16 '19 at 07:24
  • @Andre I had the same error message with a similar issue. EF Core was confusing a navigation property and throwing wrong error messages. Enabled logging on the context and found the actual error. – kRiZ Oct 13 '20 at 23:07
  • 1
    Thanks! the information you provided helped me to get an insight into my problem. – Roslin Mahmud Joy Jun 27 '22 at 09:06
  • What if I add Transient? – Saige Zhang Feb 02 '23 at 23:10
  • @SaigeZhang Transient would also work, however you'll lose the benefit of per-request entity caching by EF Core, and may result in more connections to your DB too. – Rudi Visser Feb 03 '23 at 11:30
47

This will help you!

AsNoTracking()

By default, EntityFramework tracks changes on entities that are queried and saves changes when you call SaveChanges() on the context.

If it is already tracking product:42, and you add or attach a product with the Id:42 that it hasn't seen, there is ambiguity as to which represents the source of truth.

AsNoTracking produces objects which are not added to the change tracking mechanism, which you should use if you aren't using the change tracking features, because it's faster and makes your intent more explicit.

Most often, this is a scope issue. You don't expect there to be something in the cache, but surprise, there is - usually because of some global variable or a context which was not disposed - Both very common. When you find the offending DbContext, either wrap it in a 'using' or add AsNoTracking.

What difference does .AsNoTracking() make?

Parrhesia Joe
  • 1,327
  • 12
  • 12
Ehis Anthony
  • 521
  • 4
  • 2
  • 4
    You should not disable change tracking if you want to manipulate entity instances and persist those changes to the database using SaveChanges(). https://learn.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.dbcontextoptionsbuilder.usequerytrackingbehavior?view=efcore-3.1 –  cickness Jan 13 '21 at 19:35
15

In Dotnet 6 you can just add this to your dbcontext options in the program.cs file:

builder.Services.AddDbContext<AppDbContext>(options =>
{
    options.UseSqlServer(connectionString);
    options.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
});
Tyrone Harding
  • 187
  • 1
  • 4
11

Just like Rudi Visser suggested, you can face this issue when using one entity across multiple data contexts. In my case, it was something similar to this:

User user = await DataContext.Users.SingleOrDefaultAsync(u => u.Id == "SomeId");

// Switch to UserManager *_*
var res = await UserManager.ResetPasswordAsync(user, token, password); // Exception thrown

A simple solution would be to try to stick to one DataContext, because UserManager seems to do some attaching and detaching (maybe) in the background. For example:

User user = await UserManager.FindByIdAsync("SomeId");
var res = await UserManager.ResetPasswordAsync(user, token, password);
Prince Owen
  • 1,225
  • 12
  • 20
  • 1
    After 3 hours of debugging, came across your solution! In my case, I was using a user entity from a UserStore and was passing it to the UserManager's ResetPasswordAsync. The problem is solved after I use UserManager for both. Thanks, man! Really appreciate your help! – Farkhod Oct 28 '22 at 07:21
10

I ran into this issue, but my DbContext was already registered as Scoped. My issue was that a property was being tracked within a nested navigation property. Nulling that navigation property (leaving the foreign key) fixed this for me.

Nicholas Smith
  • 191
  • 2
  • 5
  • Thanks @Nicholas Smith, for me this solution also worked, however, thinking... wouldn't it be an Entity Framework bug? – Neias Jun 09 '22 at 17:10
  • Thanks, this helped me find my problem. I had 2 lines of EF queries. The two queries are querying different tables (so different EF entities), but one of the table has FK which makes EF adds a nested navigation property in EF. So basically the issue was that I accidentally ended up with double tracking because of nested navigation property. The solution for me was to change one of the 2 queries to AsNoTracking(). – bobt Feb 28 '23 at 19:56
9

You need Detached Entry-

Entry<CustomerBaseInfo>(data).State = EntityState.Detached;
John Smith
  • 7,243
  • 6
  • 49
  • 61
MiCoos
  • 115
  • 1
  • 2
2

I am experiencing with same problem and googled and here is my thought

Entity framework has change tracker and entities are stored and referenced in the change tracker using their primary key. More precisely, dbcontext has a secret dictionary and it only allow to store one object of same type and same primary key. It can not track two object of same type and same primary key. Here is my sample code.

var model = new EaEnrollment()
        {
            Id = 1,
            UserId = 2,
            AllocationType = "Individual",
            Status = "In Progress"
        };
        _repoEaEnrollment.Update(model);
        //here is the some other code that meets my businees requirement
        //and changes the status to Completed and trying to update type a

        model.Status = "Completed";
        _repoEaEnrollment.Update(model); // exception .ie. it can not track 
                                         //  two distinct  instance. They are distinct becuase  
                                         //they have same typeand same primary key

I solved by detaching first instance from change tracker. Here is the solution code..

 var model = new EaEnrollment()
        {
            Id = 1,
            UserId = 2,
            AllocationType = "Individual",
            Status = "In Progress"
        };
        _repoEaEnrollment.Update(model);
        _repoEaEnrollment.SaveChanges();
        //here is the some other code that meets my businees requirement
        //and changes the status to Completed and trying to update type a

        //detach first instance from change tracker
        _repoEaEnrollment.Detach(model);

        //Update again
        model.Status = "Completed";
        _repoEaEnrollment.Update(model); // no exception  
Ehasanul Hoque
  • 578
  • 8
  • 14
0

In my case I had composite keys (3 keys) in one table and in the configurations I only added 2 of the keys and missed the third one.

Once I added the third key this fixed the issue for me!

Tomerikoo
  • 18,379
  • 16
  • 47
  • 61
khaled Dehia
  • 821
  • 7
  • 13
0

I was also facing the same issue. I am on .Net 6.

I was trying to get the entity and was trying to update in the same method. You can add AsNoTracking in your get query and Update like this:

public async Task<Student> GetStudentById(long id, long schoolId)
{
    return await _context.Students.AsNoTracking()
                .FirstOrDefaultAsync(student => student.Id == id && student.SchoolId== schoolId);
}

public async Task<long> UpdateStudent(Student student)
{
    long updatedStudentId = 0;
    var studentInDb = await GetStudentById(student.Id, student.SchoolId);
    if(studentInDb != null)
    {
        _context.Students.Update(student);                
        updatedStudentId = await _context.SaveChangesAsync();
    }
    return updatedStudentId;
}
KushalSeth
  • 3,265
  • 1
  • 26
  • 29
-2

For me, the following has worked (in .net 6):

dbcontext.Remove(EntityInstance);
dbcontext.model.Update(EntityInstance);

the above has generated the update statement and ran successfully as expected.

the following also has worked:

dbcontext.model.Remove(EntityInstance);
dbcontext.model.Update(EntityInstance);
Suraj Rao
  • 29,388
  • 11
  • 94
  • 103
MKR
  • 1