"An entity object cannot be referenced by multiple instances of IEntityChangeTracker"

12,018

Solution 1

You have two different repositories that each have a separate data context - this is the root problem - your data context should be shared by all your repositories, i.e. you can inject it via constructor injection:

MyDBEntities entities = new MyDBEntities();
_vendorsRepository = new SqlVendorsRepository(entities);
_regionsRepository = new SqlRegionsRepository(entities);

Solution 2

I came across the same error when I had a Caching layer that was caching look-up Entities and retrieving them from the cache. The cached entities were proxy entities and still had a reference to the DBContext that originally retrieved them.

The main symptom of this is that the first save of a Domain entity with a look up reference worked okay, but subsequent saves failed.

I hope this helps someone, as the problem took 2 days off my life.

Share:
12,018
Steven
Author by

Steven

Updated on June 18, 2022

Comments

  • Steven
    Steven almost 2 years

    I have 3 database tables: Vendors, Regions, and VendorRegions:

    Vendors
    -------------------
    VendorID (PK)
    Name
    
    Regions
    -------------------
    RegionID (PK)
    Name
    
    VendorRegions
    -------------------
    VendorID (PK)
    RegionID (PK)
    

    On my page to create a vendor, I list out every Region in a checkbox list. For every region that is checked, I'd like to add the VendorID and RegionID to the VendorRegions table.

    I'm using Entity Framework, C# and MVC 3.

    Here's my controller code:

    public class VendorsController : Controller
    {
        readonly IVendorsRepository _vendorsRepository;
        readonly IRegionsRepository _regionsRepository;
    
        public VendorsController()
        {
            _vendorsRepository = new SqlVendorsRepository();
            _regionsRepository = new SqlRegionsRepository();
        }
    
        [HttpPost]
        public ActionResult Create(VendorViewModel viewModel, string[] Regions)
        {
            var vendor = new Vendor();
            TryUpdateModel(vendor);
    
            if (ModelState.IsValid)
            {
                vendor.Regions.Clear();
                foreach (var regionId in Regions)
                {
                    vendor.Regions.Add(_regionsRepository.GetRegion(Int32.Parse(regionId)));
                }
    
                _vendorsRepository.SaveVendor(vendor);
                return RedirectToAction("Index");
            }
    
            return View(viewModel);     // validation error, so redisplay same view
        }
    }
    

    Here's my Vendors repository code:

    public class SqlVendorsRepository : IVendorsRepository
    {
        private readonly MyDBEntities _entities;
    
        public SqlVendorsRepository()
        {            
            _entities = new MyDBEntities();
        }
    
    
        public void SaveVendor(Vendor vendor)
        {
            // If it's a new vendor, just attach it to the DataContext
            if (vendor.VendorID == 0)
                _entities.Vendors.Context.AddObject("Vendors", vendor); // ERROR HERE
            else if (vendor.EntityState == EntityState.Detached)
            {
                // We're updating an existing vendor, but it's not attached to this data context, so attach it and detect the changes
                _entities.Vendors.Context.Attach(vendor);
                _entities.Vendors.Context.Refresh(System.Data.Objects.RefreshMode.ClientWins, vendor);
            }
            _entities.Vendors.Context.SaveChanges();
        }
    }
    

    The error occurs when I try to save my vendor, at _entities.Vendors.Context.AddObject("Vendors", vendor);. And just in case, here's my Regions repository code:

    public class SqlRegionsRepository : IRegionsRepository
    {
        private readonly MyDBEntities _entities;
    
        public SqlRegionsRepository()
        {            
            _entities = new MyDBEntities();
        }
    
    
        public IQueryable<Region> Regions
        {
            get { return _entities.Regions.AsQueryable(); }
        }
    
    
        public Region GetRegion(int id)
        {
            return Regions.FirstOrDefault(st => st.RegionID == id);
        }
    }
    

    This seems like a simple thing to do, but I don't know how to get past this error. Any ideas?

  • Hanna
    Hanna almost 12 years
    Is there any chance you could give a basic example?
  • LPQ
    LPQ over 2 years
    I know it's been a long time but this sounds like what I'm experiencing and I'm not sure of the best way to solve it. I am using caching on my repository, and the second update is loading the cached record and trying to update the database with it's change but fails. What did you do to resolve? I'd like to keep my caching layer, and I thought it was the correct way to handle repository caching.
  • Daniel Dyson
    Daniel Dyson over 2 years
    It has been about a year since I used EF, but I'll do my best to answer. Your caching should not be on your Repo. An Entity in EF is a populated in memory object that is only fetched from DB once per context. So it is effectively a cache already. So if you call SaveChanges() on your context, you need to retrieve a 'fresh' copy of the entity again before updating it. How is the Entity being updated? With an api or a view model or other type of DTO? If so, maybe that is where you would benefit from some caching if it is really necessary, but then you will need to deal with cache invalidation.
  • Daniel Dyson
    Daniel Dyson over 2 years
    Also, if you are retrieving your entities with trackchanges off, you might find this useful: stackoverflow.com/a/36684660/230428