2009-03-29 5 views
19

J'essaie de sauvegarder mon contact, qui a des références à ContactRelation (juste la relation du contact, marié, célibataire, etc.) et Country. Mais chaque fois que je tente de sauver mon contact, qui est validé je reçois l'exception « ADO.Net Entity Framework Un objet entité ne peut pas être référencée par plusieurs instances de IEntityChangeTracker »ADO.Net Entity Framework Un objet entité ne peut pas être référencé par plusieurs instances de IEntityChangeTracker

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

J'utilise un couplage lâche MVC concevoir avec des services et des dépôts. J'ai lu beaucoup de commentaires sur cette exception, mais aucun ne me donner une réponse de travail ...

Merci, Peter

Répondre

21

[Mise à jour]
Parce que L2E est utilisé, vous devez d'abord enregistrer tous les objets liés avant de pouvoir enregistrer l'objet principal. Ce qui a du sens sinon tu créerais (dans mon exemple) un artiste sans son objet contact. Ceci n'est pas autorisé par la conception de la base de données.
[/ Mise à jour]

Voici ma mise en œuvre qui a fonctionné.

[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"); 
} 

Et puis dans mon ContactRepository:

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

J'ai aussi trouvé sur ce site qu'il est préférable de garder le même contexte dans l'application, donc je suis maintenant avec une classe de données spéciale pour ceci:

Rick Strahl et Samuel Maecham m'ont appris que vous devriez garder votre datacontext par utilisateur par demande. Ce qui signifie le mettre dans le HttpContext pour les applications web. Lisez-tout 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; 
     } 
    } 
} 
+1

Merci Peter, je passe environ une heure à essayer de trouver la même solution. Merci encore – Geo

5

Je l'ai vu avant, vous devez convertir le champ de référence à un EntityKey avant d'enregistrer, puis Chargez-le après son enregistré. Essayez ce code à la place:

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); 
    } 
} 

Bien sûr, une partie de ce code devra être modifié en fonction de la structure de votre base de données exacte.

+0

merci de m'indiquer dans la bonne direction, êtes-vous d'accord si je définis ma propre réponse comme réponse acceptée? – Peter

+0

Si je l'ai aidé, vous pouvez me lancer un vote. Votre bienvenue pour vous marquer comme la réponse choisie, mais vous n'obtiendrez aucun représentant pour cela. – bendewey

+0

Je le ferai dès que j'aurai assez de réputation, merci! – Peter

2

Hummm Je me demande si ce que quelqu'un peut vérifier ma santé mentale solution. C'est très similaire à la réponse acceptée ci-dessous mais après avoir lu le blog de Rick Strahl à propos du DataContext Life Management, je crains que ce ne soit pas une solution thread-safe pour une application web.

J'ai également contourné l'instance où je recevais ce message d'erreur en accédant à mon contexte d'objet en utilisant le modèle singleton.

J'ajouté ce qui suit à MyObjectContext classe:

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

Et dans mon dépôt par entité mappeur au lieu d'instancier une nouvelle instance de MyObjectContext J'utilise

var db = MyObjectContext.getInstance(); 

Suis-je stupide ici?Cela semble fonctionner.

+1

Vous n'avez pas tort, le singleton DataContext dans ma réponse est un mauvais design. Vous devriez faire le singleton DataContext mais par utilisateur par demande. C'est-à-dire que vous devriez le mettre dans le HttpContext pour une application Web. voir ce post: http://samscode.com/index.php/2009/12/making-entity-framework-v1-work-part-1-datacontext-lifetime-management/ – Peter

+0

fantastique, merci – user427875

Questions connexes