187

I am trying to save Employee details, which has references with City. But everytime I try to save my contact, which is validated I get the exception "ADO.Net Entity Framework An entity object cannot be referenced by multiple instances of IEntityChangeTracker"

I had read so many post but still not getting the exact idea of what to do... my Save button click code is given below

protected void Button1_Click(object sender, EventArgs e)
    {
        EmployeeService es = new EmployeeService();
        CityService cs = new CityService();

        DateTime dt = new DateTime(2008, 12, 12);
        Payroll.Entities.Employee e1 = new Payroll.Entities.Employee();

        Payroll.Entities.City city1 = cs.SelectCity(Convert.ToInt64(cmbCity.SelectedItem.Value));

        e1.Name = "Archana";
        e1.Title = "aaaa";
        e1.BirthDate = dt;
        e1.Gender = "F";
        e1.HireDate = dt;
        e1.MaritalStatus = "M";
        e1.City = city1;        

        es.AddEmpoyee(e1,city1);
    }

and Employeeservice Code

public string AddEmpoyee(Payroll.Entities.Employee e1, Payroll.Entities.City c1)
        {
            Payroll_DAO1 payrollDAO = new Payroll_DAO1();
            payrollDAO.AddToEmployee(e1);  //Here I am getting Error..
            payrollDAO.SaveChanges();
            return "SUCCESS";
        }
wonea
  • 4,783
  • 17
  • 86
  • 139
Smily
  • 2,646
  • 4
  • 23
  • 31

13 Answers13

256

Because these two lines ...

EmployeeService es = new EmployeeService();
CityService cs = new CityService();

... don't take a parameter in the constructor, I guess that you create a context within the classes. When you load the city1...

Payroll.Entities.City city1 = cs.SelectCity(...);

...you attach the city1 to the context in CityService. Later you add a city1 as a reference to the new Employee e1 and add e1 including this reference to city1 to the context in EmployeeService. As a result you have city1 attached to two different context which is what the exception complains about.

You can fix this by creating a context outside of the service classes and injecting and using it in both services:

EmployeeService es = new EmployeeService(context);
CityService cs = new CityService(context); // same context instance

Your service classes look a bit like repositories which are responsible for only a single entity type. In such a case you will always have trouble as soon as relationships between entities are involved when you use separate contexts for the services.

You can also create a single service which is responsible for a set of closely related entities, like an EmployeeCityService (which has a single context) and delegate the whole operation in your Button1_Click method to a method of this service.

Slauma
  • 175,098
  • 59
  • 401
  • 420
  • 5
    I like the way you figured it out even if answer didn't include some background information. – Daniel Kmak Jul 12 '14 at 13:43
  • Looks like this will solve my problem, I just have no idea how to write the new context instance :( – Ortund Jan 27 '15 at 16:36
  • 12
    Abstracting ORM is like putting yellow lipstick on a turd. – Ronnie Overby May 24 '16 at 14:28
  • I might be missing something here, but in some ORMs (especially EntityFramework) the data context should always be shortlived. Introducing a static or re-used context will introduce a whole other set of challenges and problems. – Maritim Oct 13 '16 at 14:02
  • @Maritim it depends on the usage. In Web Applications, its typically one roundtrip. In Desktop Applications, you might also use one per `Form` (what ever, it just represents one unit of work) per `Thread` (because `DbContext` is not guaranteed to be threadsafe). – LuckyLikey Aug 22 '17 at 14:26
  • Ah, so is a valid summary "don't cross the streams"? All linking needs to be on one instance or the other? – Robin Vessey Mar 27 '19 at 06:28
  • Does that mean the context has a lifespan at minimum equal to the sum of `EmployeeService` and `CityService` ? Isn't long-running `DbContext` frowned upon ? – Arthur Attout Dec 22 '20 at 15:34
35

Steps to reproduce can be simplified to this:

var contextOne = new EntityContext();
var contextTwo = new EntityContext();

var user = contextOne.Users.FirstOrDefault();

var group = new Group();
group.User = user;

contextTwo.Groups.Add(group);
contextTwo.SaveChanges();

Code without error:

var context = new EntityContext();

var user = context.Users.FirstOrDefault();

var group = new Group();
group.User = user; // Be careful when you set entity properties. 
// Be sure that all objects came from the same context

context.Groups.Add(group);
context.SaveChanges();

Using only one EntityContext can solve this. Refer to other answers for other solutions.

GabLeRoux
  • 16,715
  • 16
  • 63
  • 81
Pavel Shkleinik
  • 6,298
  • 2
  • 24
  • 36
  • 2
    lets say you wanted to use contextTwo? (maybe due to scope issues or something) how do you detach from contextOne and attach to contextTwo? – NullVoxPopuli Jan 20 '16 at 13:24
  • If you need to do smth like this, most likely you are doing this in wrong way... I suggest using one context. – Pavel Shkleinik Jan 20 '16 at 21:18
  • 4
    There are instances where you would want to use a different instance, such as when pointing into a different database. – Jay Nov 22 '16 at 11:16
  • 1
    This is a helpful simplification of the problem; but it does not provide a real answer. – BrainSlugs83 Mar 10 '17 at 02:04
9

This is an old thread, but another solution, which I prefer, is just update the cityId and not assign the hole model City to Employee... to do that Employee should look like:

public class Employee{
    ...
    public int? CityId; //The ? is for allow City nullable
    public virtual City City;
}

Then it's enough assigning:

e1.CityId=city1.ID;
GabLeRoux
  • 16,715
  • 16
  • 63
  • 81
user3484623
  • 111
  • 1
  • 4
5

Alternatively to injection and even worse Singleton, you can call Detach method before Add.

EntityFramework 6: ((IObjectContextAdapter)cs).ObjectContext.Detach(city1);

EntityFramework 4: cs.Detach(city1);

There is yet another way, in case you don't need first DBContext object. Just wrap it with using keyword:

Payroll.Entities.City city1;
using (CityService cs = new CityService())
{
  city1 = cs.SelectCity(Convert.ToInt64(cmbCity.SelectedItem.Value));
}
Roman O
  • 3,172
  • 30
  • 26
  • 1
    I used the following: `dbContext1.Entry(backgroundReport).State = System.Data.Entity.EntityState.Detached`' to detach and then was able to use `dbContext2.Entry(backgroundReport).State = System.Data.Entity.EntityState.Modified;` to update. Worked like a dream – Peter Smith Feb 17 '15 at 16:14
  • Yes, Peter. I should mention to mark State as Modified. – Roman O Feb 18 '15 at 19:38
  • In my application startup (global.asax) logic I was loading up a list of widgets.. a simple list of reference objects I stash into memory. Since I was doing my EF context inside Using statements, I thought there'd be no issue later when my controller got around to assigning those objects into a business graph (hey, that old context is gone, right?) - this answer saved me. – bkwdesign Aug 31 '15 at 20:20
4

I had the same problem but my issue with the @Slauma's solution (although great in certain instances) is that it recommends that I pass the context into the service which implies that the context is available from my controller. It also forces tight coupling between my controller and service layers.

I'm using Dependency Injection to inject the service/repository layers into the controller and as such do not have access to the context from the controller.

My solution was to have the service/repository layers use the same instance of the context - Singleton.

Context Singleton Class:

Reference: http://msdn.microsoft.com/en-us/library/ff650316.aspx
and http://csharpindepth.com/Articles/General/Singleton.aspx

public sealed class MyModelDbContextSingleton
{
  private static readonly MyModelDbContext instance = new MyModelDbContext();

  static MyModelDbContextSingleton() { }

  private MyModelDbContextSingleton() { }

  public static MyModelDbContext Instance
  {
    get
    {
      return instance;
    }
  }
}  

Repository Class:

public class ProjectRepository : IProjectRepository
{
  MyModelDbContext context = MyModelDbContextSingleton.Instance;
  [...]

Other solutions do exist such as instantiating the context once and passing it into the constructors of your service/repository layers or another I read about which is implementing the Unit of Work pattern. I'm sure there are more...

GabLeRoux
  • 16,715
  • 16
  • 63
  • 81
kmullings
  • 117
  • 2
  • 6
  • 9
    ...doesn't this break down as soon as you try and use multithreading? – CaffGeek May 24 '13 at 17:01
  • 9
    A context should not remain open any longer than necessary, using a Singleton to keep it open forever is the very last thing you want to do. – enzi Jul 30 '13 at 07:48
  • 3
    I have seen good implementations of this per request. Using the Static keyword is wrong but if you make this pattern to instantiate the context at the beginning of the request and dispose it at the end of request, it would be a legit solution. – Aidin Dec 13 '13 at 13:57
  • 1
    This is really bad advice. If you're using DI (I don't see the evidence here?) then you should let your DI container manage the context lifetime and it should probably be per-request. – Casey Apr 25 '14 at 20:18
  • Supporting comments above. This solution has side effects. – Isaac Llopis Jul 30 '14 at 09:20
  • 3
    This is BAD. BAD. BAD. BAD. BAD. Particularly if this is a web application, since static objects are shared between all threads and users. This means that multiple simultaneous users of your website will be stomping on your data context, potentially corrupting it, saving changes you didn't intend, or even just creating random crashes. DbContexts should NEVER be shared across threads. Then there's the problem that statics never get destroyed, so it will sit and keep using more and more memory... – Erik Funkenbusch Dec 31 '14 at 21:18
  • Great feedback. What do you think of @Aidin's suggestion? – kmullings Jan 05 '15 at 00:30
3

In my case, I was using the ASP.NET Identity Framework. I had used the built in UserManager.FindByNameAsync method to retrieve an ApplicationUser entity. I then tried to reference this entity on a newly created entity on a different DbContext. This resulted in the exception you originally saw.

I solved this by creating a new ApplicationUser entity with only the Id from the UserManager method and referencing that new entity.

Justin Skiles
  • 9,373
  • 6
  • 50
  • 61
2

I hit this same problem after implementing IoC for a project (ASP.Net MVC EF6.2).

Usually I would initialise a data context in the constructor of a controller and use the same context to initialise all my repositories.

However using IoC to instantiate the repositories caused them all to have separate contexts and I started getting this error.

For now I've gone back to just newing up the repositories with a common context while I think of a better way.

Richard Moore
  • 1,133
  • 17
  • 25
2

This is how I encountered this issue. First I need to save my Order which needs a reference to my ApplicationUser table:

  ApplicationUser user = new ApplicationUser();
  user = UserManager.FindById(User.Identity.GetUserId());

  Order entOrder = new Order();
  entOrder.ApplicationUser = user; //I need this user before saving to my database using EF

The problem is that I am initializing a new ApplicationDbContext to save my new Order entity:

 ApplicationDbContext db = new ApplicationDbContext();
 db.Entry(entOrder).State = EntityState.Added;
 db.SaveChanges();

So in order to solve the problem, I used the same ApplicationDbContext instead of using the built-in UserManager of ASP.NET MVC.

Instead of this:

user = UserManager.FindById(User.Identity.GetUserId());

I used my existing ApplicationDbContext instance:

//db instance here is the same instance as my db on my code above.
user = db.Users.Find(User.Identity.GetUserId()); 
Willy David Jr
  • 8,604
  • 6
  • 46
  • 57
1

I had the same problem and I could solve making a new instance of the object that I was trying to Update. Then I passed that object to my reposotory.

1

In this case, it turns out the error is very clear: Entity Framework cannot track an entity using multiple instances of IEntityChangeTracker or typically, multiple instances of DbContext. The solutions are: use one instance of DbContext; access all needed entities through a single repository (depending on one instance of DbContext); or turning off tracking for all entities accessed via a repository other than the one throwing this particular exception.

When following an inversion of control pattern in .Net Core Web API, I frequently find that I have controllers with dependencies such as:

private readonly IMyEntityRepository myEntityRepo; // depends on MyDbContext
private readonly IFooRepository fooRepo; // depends on MyDbContext
private readonly IBarRepository barRepo; // depends on MyDbContext
public MyController(
    IMyEntityRepository myEntityRepo, 
    IFooRepository fooRepo, 
    IBarRepository barRepo)
{
    this.fooRepo = fooRepo;
    this.barRepo = barRepo;
    this.myEntityRepo = myEntityRepo;
}

and usage like

...
myEntity.Foo = await this.fooRepository.GetFoos().SingleOrDefaultAsync(f => f.Id == model.FooId);
if (model.BarId.HasValue)
{
    myEntity.Foo.Bar = await this.barRepository.GetBars().SingleOrDefaultAsync(b => b.Id == model.BarId.Value);
}

...
await this.myEntityRepo.UpdateAsync(myEntity); // this throws an error!

Since all three repositories depend on different DbContext instances per request, I have two options to avoid the problem and maintain separate repositories: change the injection of the DbContext to create a new instance only once per call:

// services.AddTransient<DbContext, MyDbContext>(); <- one instance per ctor. bad
services.AddScoped<DbContext, MyDbContext>(); // <- one instance per call. good!

or, if the child entity is being used in a read-only manner, turning off tracking on that instance:

myEntity.Foo.Bar = await this.barRepo.GetBars().AsNoTracking().SingleOrDefault(b => b.Id == model.BarId);
Kjata30
  • 721
  • 7
  • 20
0

Use the same DBContext object throughout the transaction.

Nalan Madheswaran
  • 10,136
  • 1
  • 57
  • 42
0

For my scenario we have a solution with several applications referencing the same context. I had to update the unity.config file adding lifetime type to the context.

<lifetime type="PerResolveLifetimeManager" />
Michael
  • 239
  • 3
  • 9
-3

Error source:

ApplicationUser user = await UserManager.FindByIdAsync(User.Identity.Name);
ApplicationDbContext db = new ApplicationDbContent();
db.Users.Uploads.Add(new MyUpload{FileName="newfile.png"});
await db.SavechangesAsync();/ZZZZZZZ

Hope someone saves some precious time

Mike G
  • 4,232
  • 9
  • 40
  • 66