2009-03-29 15 views
19

Estoy tratando de guardar mi contacto, que tiene referencias a ContactRelation (solo la relación del contacto, casado, soltero, etc.) y País. Pero cada vez que intento salvar a mi contacto, que se valida consigo la excepción "ADO.Net Entity Framework Un objeto entidad no puede ser referenciado por varias instancias de IEntityChangeTracker"Entidad de Entidades ADO.Net Un objeto de entidad no puede ser referenciado por varias instancias de IEntityChangeTracker

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

que estoy usando un MVC débilmente acoplado diseño con Servicios y Repositorios. He leído un montón de mensajes sobre esta excepción, pero ninguno me da una respuesta de trabajo ...

Gracias, Peter

Respuesta

21

[Actualización]
Debido L2E se utiliza es necesario guardar todos los objetos vinculados antes de poder guardar el objeto principal. Lo cual tiene sentido; de lo contrario, crearías (en mi ejemplo) un artista sin su objeto de contacto. Esto no está permitido por el diseño de la base de datos.
[/ Actualización]

Aquí está mi aplicación que funcionó.

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

Y luego en mi ContactRepository:

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

También encontré en este sitio web que lo mejor es mantener el mismo contexto en toda la aplicación, así que estoy usando ahora una clase de datos especial para esto:

Rick Strahl y Samuel Maecham me han enseñado que debe mantener su datacontext por usuario por solicitud. Lo que significa ponerlo en el HttpContext para aplicaciones web. Leer todo sobre ella 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

Gracias Peter, me paso alrededor de una hora tratando de buscar la misma solución. Gracias de nuevo – Geo

5

que he visto esto antes, puede que tenga que convertir el campo de referencia a una EntityKey antes de guardar y luego cargarlo después de guardarlo. Prueba este código en su lugar:

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

Por supuesto, parte de este código puede ser necesario cambiar dependiendo de su estructura de base de datos exacta.

+0

gracias por indicarme la dirección correcta, ¿está bien si configuro mi propia respuesta como la aceptada? – Peter

+0

Si le ayudé, puede votarme. Le invitamos a marcarse a usted mismo como la respuesta seleccionada, pero no obtendrá ningún representante por ello. – bendewey

+0

¡Tan pronto como tenga suficiente reputación, gracias! – Peter

2

Ummm me pregunto si alguien puede complacer a la cordura comprobar mi solución. Es muy similar a la respuesta aceptada a continuación, pero después de leer el blog de Rick Strahl sobre DataContext Life Management, me preocupa que esta no sea una solución segura para subprocesos para una aplicación web.

También encontré la instancia en la que estaba recibiendo este mensaje de error accediendo al contexto de mi objeto utilizando el patrón singleton.

que añade lo siguiente al MyObjectContext clase:

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

Y en mi asignador repositorio per entidad en lugar de crear una nueva instancia de MyObjectContext utilizo

var db = MyObjectContext.getInstance(); 

Estoy siendo estúpida aquí?Parece funcionar.

+1

No estás equivocado, el singleton DataContext en mi respuesta es un mal diseño. Debe hacer el Singleton de DataContext pero por usuario por solicitud. es decir, debe colocarlo en HttpContext para una aplicación web. ver esta entrada: http://samscode.com/index.php/2009/12/making-entity-framework-v1-work-part-1-datacontext-lifetime-management/ – Peter

+0

fantástico, gracias – user427875

Cuestiones relacionadas