183 votes

l'objet entité ne peut pas être référencé par plusieurs instances de IEntityChangeTracker. lors de l'ajout d'objets connexes à l'entité dans Entity Framework 4.1

J'essaie de sauvegarder les détails de l'employé, qui a des références avec la ville. Mais chaque fois que j'essaie de sauvegarder mon contact, qui est validé, je reçois l'exception suivante "ADO.Net Entity Framework Un objet d'entité ne peut être référencé par plusieurs instances de IEntityChangeTracker"

J'ai lu de nombreux articles, mais je n'ai toujours pas trouvé l'idée exacte de ce qu'il faut faire... mon code de clic du bouton Save est donné ci-dessous

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

et Code du service des employés

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

253voto

Slauma Points 76561

Parce que ces deux lignes...

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

... ne prennent pas de paramètre dans le constructeur, je suppose que vous créez un contexte dans les classes. Lorsque vous chargez les city1 ...

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

...vous attachez le city1 au contexte dans CityService . Plus tard, vous ajoutez un city1 comme référence à la nouvelle Employee e1 et ajouter e1 y compris cette référence à city1 au contexte dans EmployeeService . En conséquence, vous avez city1 attachés à deux contextes différents, ce qui est ce dont l'exception se plaint.

Vous pouvez résoudre ce problème en créant un contexte en dehors des classes de service et en l'injectant et l'utilisant dans les deux services :

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

Vos classes de service ressemblent un peu à des référentiels qui ne sont responsables que d'un seul type d'entité. Dans un tel cas, vous aurez toujours des problèmes dès que des relations entre entités sont impliquées lorsque vous utilisez des contextes séparés pour les services.

Vous pouvez également créer un service unique qui est responsable d'un ensemble d'entités étroitement liées, comme une EmployeeCityService (qui a un seul contexte) et déléguer l'ensemble de l'opération dans votre Button1_Click à une méthode de ce service.

5 votes

J'aime la façon dont tu as trouvé la solution, même si la réponse n'a pas inclus d'informations de base.

0 votes

On dirait que cela va résoudre mon problème, je n'ai juste aucune idée de comment écrire la nouvelle instance de contexte :(

12 votes

Abstraire ORM, c'est comme mettre du rouge à lèvres jaune sur un étron.

32voto

Pavel Shkleinik Points 329

Les étapes de la reproduction peuvent être simplifiées comme suit :

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 sans erreur :

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

En utilisant un seul EntityContext peut résoudre ce problème. Reportez-vous aux autres réponses pour d'autres solutions.

9voto

user3484623 Points 111

Il s'agit d'un vieux fil de discussion, mais une autre solution, que je préfère, est de simplement mettre à jour le cityId et de ne pas affecter le modèle de trou City à l'employé... pour ce faire, Employee devrait ressembler à ceci :

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

Ensuite, il suffit d'assigner :

e1.CityId=city1.ID;

5voto

Roman O Points 312

Alternativement à l'injection et encore pire à Singleton, vous pouvez appeler Détacher avant d'ajouter.

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

EntityFramework 4 : cs.Detach(city1);

Il existe encore une autre solution, au cas où vous n'auriez pas besoin du premier objet DBContext. Il suffit de l'envelopper avec en utilisant mot-clé :

Payroll.Entities.City city1;
using (CityService cs = new CityService())
{
  city1 = cs.SelectCity(Convert.ToInt64(cmbCity.SelectedItem.Value));
}

4voto

kmullings Points 49

J'ai eu le même problème, mais ce que je reproche à la solution de @Slauma (bien qu'elle soit excellente dans certains cas), c'est qu'elle recommande de passer le contexte dans le service, ce qui implique que le contexte soit disponible dans mon contrôleur. Cela oblige également à un couplage étroit entre les couches contrôleur et service.

J'utilise l'injection de dépendances pour injecter les couches de service/référentiel dans le contrôleur et, en tant que tel, je n'ai pas accès au contexte depuis le contrôleur.

Ma solution consistait à faire en sorte que les couches service/référentiel utilisent la même instance du contexte - Singleton.

Contexte Classe Singleton :

Référence : http://msdn.microsoft.com/en-us/library/ff650316.aspx
et 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;
    }
  }
}  

Classe de dépôt :

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

D'autres solutions existent, comme l'instanciation du contexte une fois et son passage dans les constructeurs de vos couches de service/référentiel, ou une autre que j'ai lue, qui consiste à mettre en œuvre le modèle Unit of Work. Je suis sûr qu'il y en a d'autres...

9 votes

...cela ne s'effondre-t-il pas dès que l'on essaie d'utiliser le multithreading ?

9 votes

Un contexte ne doit pas rester ouvert plus longtemps que nécessaire. Utiliser un Singleton pour le garder ouvert pour toujours est la toute dernière chose à faire.

3 votes

J'ai vu de bonnes implémentations de ceci par requête. L'utilisation du mot-clé Static n'est pas correcte mais si vous faites ce modèle pour instancier le contexte au début de la requête et le disposer à la fin de la requête, ce serait une solution légitime.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X