Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ADO.Net Entity Framework An entity object cannot be referenced by multiple instances of IEntityChangeTracker

I am trying to save my contact, which has references to ContactRelation (just the relationship of the contact, married, single, etc) and Country. 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"

public Contact CreateContact(Contact contact)
{
    _entities.AddToContact(contact); //throws the exception
    _entities.SaveChanges();
    return contact ;
}

I'm using a loosely coupled MVC design with Services and Repositories. I've read a lot of posts about this exception but none give me a working answer...

Thank you, Peter

like image 718
Peter Avatar asked Mar 29 '09 13:03

Peter


3 Answers

[Update]
Because L2E is used you need to save all the linked objects first before you can save the main object. Which makes sense otherwise you would create (in my example) an artist without it's contact object. This isn't allowed by the database design.
[/Update]

Here's my implementation which worked.

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create([Bind(Exclude = "Id")] Artist artist, [Bind(Prefix = "Contact")] Contact contact, [Bind(Prefix = "Country")] Country country, [Bind(Prefix = "ContactRelationship")] ContactRelationship contactRelationship)
{
    ViewData["Countries"] = new SelectList(new CountryService(_msw).ListCountries().OrderBy(c => c.Name), "ID", "Name");
    ViewData["ContactRelationships"] = new SelectList(new ContactRelationshipService(_msw).ListContactRelationships().OrderBy(c => c.ID), "ID", "Description");

    country = _countryService.GetCountryById(country.ID);
    contact.Country = country;
    contactRelationship = _contactRelationshipService.GetContactRelationship(contactRelationship.ID);
    contact.ContactRelationship = contactRelationship;
    if(_contactService.CreateContact(contact)){
        artist.Contact = contact;
        if (_service.CreateArtist(artist))
            return RedirectToAction("Index");        
    }
    return View("Create");
}

And then in my ContactRepository :

public Contact CreateContact(Contact contact)
{
    _entities.AddToContact(contact); //no longer throws the exception
    _entities.SaveChanges();
    return contact ;
}

I also found on this website that it is best to keep the same context throughout the application so I'm now using a special Data class for this:

Rick Strahl and Samuel Maecham have taught me that you should keep your datacontext per user per request. Which means putting it in the HttpContext for web applications. Read all about it here

public class Data
{
    public static MyDBEntities MyDBEntities
    {
        get
        {
            if (HttpContext.Current != null && HttpContext.Current["myDBEntities"] == null)
            {
                HttpContext.Current["myDBEntities"] = new MyDBEntities ();
            }
            return HttpContext.Current["myDBEntities"] as MyDBEntities;
        }
        set { 
            if(HttpContext.Current != null)
                HttpContext.Current["myDBEntities"] = value; 
        }
    }
}
like image 188
Peter Avatar answered Nov 11 '22 16:11

Peter


I've seen this before, you may have to convert the Reference field to an EntityKey before saving and then Load it after its saved. Try this code instead:

public Contact CreateContact(Contact contact){
    contact.ConvertContactRelationToReference();
    _entities.AddToContact(contact); 
    //throws the exception
    _entities.SaveChanges();
    contact.ContactRelation.Load();
    return contact;
}

public partial class Contact
{
  public void ConvertContactRelationToReference()
  {
    var crId = ContactRelation.Id;
    ContactRelation = null;
    ContactRelationReference.EntityKey = new EntityKey("MyEntities.ContactRelations", "Id", crId);
  }
}

Of course, some of this code may need to be changed depending on your exact database structure.

like image 5
bendewey Avatar answered Nov 11 '22 17:11

bendewey


Ummm I wonder if someone can please sanity check my solution. It's very similar to the accepted answer below but after reading Rick Strahl's blog about DataContext Life Management I am worried that this isn't a thread-safe solution for a web application.

I also got round the instance where I was receiving this error message by accessing my object context using the singleton pattern.

I added the following to MyObjectContext class:

// singleton
private static MyObjectContext context;
public static MyObjectContext getInstance()
{
    if (context == null)
    {
        context = new MyObjectContext ();
    }
    return context;
} 

And in my per-entity repository mapper instead of instantiating a new instance of MyObjectContext I use

var db = MyObjectContext.getInstance();

Am I being stupid here? It seems to work.

like image 2
user427875 Avatar answered Nov 11 '22 17:11

user427875