2016-09-05 1 views
3

J'essaye de faire un clone profond de certaines entités. L'approche mentionnée dans this article a l'air bien mais je rencontre des erreurs. Il suggère d'utiliser AsNoTracking() pour récupérer l'entité, puis de la réinsérer dans le contexte où elle entraînera une insertion car elle ressemble à un nouvel objet.Problème d'utilisation d'AsNoTracking avec EntityFramework

Voici mon code:

 Using ctxt As New ProductionDataEntities 
      Dim grade = ctxt.Grades.Include(Function(g) g.GradeWidths).AsNoTracking.First 
      ctxt.Grades.AddObject(grade) 
      ctxt.SaveChanges() 
     End Using 

Mais quand je le lance je reçois:

Un objet avec la même clé existe déjà dans le ObjectStateManager. L'objet existant est dans l'état Modifié. Un objet ne peut être ajouté à ObjectStateManager à nouveau que s'il est dans l'état ajouté.

Et lorsque je modifie grade.Name, EntityState est modifié pour indiquer qu'il fait l'objet d'un suivi. J'utilise EF5 Db-First.

J'ai également essayé de cloner en détachant le grade et en le réinsérant, cela fonctionne mais les Largeurs de Grade ne sont pas copiées. Dès que je l'appelle détachez le nombre gradewidth va de 2 à 0.

Questions:

  1. Toute idée pourquoi AsNoTracking ne fonctionne pas et ce que je peux faire pour résoudre ce problème?
  2. Une autre approche peut-elle être recommandée pour un clonage simple en profondeur?

Merci.

---- Informations complémentaires ----

J'ai 5 1-à-plusieurs et, finalement, je vais à cloner à partir du haut niveau tout en bas. Mais je simplifie pour regarder le niveau le plus bas.

  • 1 e année à de nombreux GradeWidths
+0

Vous vouloir attacher l'entité au contexte, ne pas l'ajouter. L'ajout créerait seulement une nouvelle ligne dans la table quand même. – DavidG

+0

@DavidG - J'essaie de créer une nouvelle ligne (c'est-à-dire Dupliquer). En outre, je ne peux pas appeler joindre car l'entité est déjà attachée malgré que j'appelle AsNoTracking – FloatingKiwi

+2

Ah je devrais l'avoir lu! Ensuite, vous avez probablement juste besoin de blitz la valeur de la clé primaire. – DavidG

Répondre

1

En utilisant ObjectContext

En utilisant ObjectContext vous pouvez d'abord attacher l'objet parent, puis changer d'état d'objet ajouté. Aussi pour chaque objet enfant, changez l'état de l'objet à ajouter. Vous n'avez même pas besoin de AsNoTracking().

Par exemple, pour une relation Category(1)↔(N)Product J'ai utilisé ce code:

Using db As New SampleSystemEntities 
    Dim c = db.Categories.Include(Function(x) x.Products).First 
    db.Attach(c) 
    db.ObjectStateManager.ChangeObjectState(c, EntityState.Added) 
    For Each p As Product In c.Products 
     db.ObjectStateManager.ChangeObjectState(p, EntityState.Added) 
    Next 
    db.SaveChanges() 
End Using 

En utilisant DbContext

Si vous utilisez DbContext tout fonctionnera bien en utilisant votre code:

Using db As New SampleSystemEntities 
    Dim c = db.Categories.Include(Function(x) x.Products).AsNoTracking().First 
    db.Categories.Add(c) 
    db.SaveChanges() 
End Using 
+0

Merci Reza. C'est une excellente alternative qui fonctionne pour moi. Je suis toujours intéressé par la raison pour laquelle AsNoTracking ne fonctionne pas sur mon PC, donc je vais laisser la question ouverte quelques jours de plus. – FloatingKiwi

+0

Pas de problème, je partagerai aussi plus si j'avais autre chose à partager :) –

+0

Si vous utilisez 'DbContext' alors' AsNoTracking' fonctionnera comme prévu. Avez-vous des exigences qui vous obligent à utiliser 'AsNoTracking'? –

0

Sans le modèle est assez difficile de comprendre le comportement exact (dans ce cas, les clés primaires sont importants).
En outre, je n'utilise pas EF 5 mais EF 6, il devrait être assez similaire.

Dans votre application, vous lisez une entité avec des entités associées (GradeWidths) à partir de la base de données et vous devez l'ajouter à la base de données. Je suppose que votre modèle est 1 Grade n GradeWidths et que vous créez de nouvelles instances de GradeWidths copiées à partir du Grade d'origine (votre modèle pourrait être nm et dans ce cas vous pourriez utiliser 1 GradeWidth sur plus de Grades mais je suppose que non) .

Dans ce cas, pour ajouter de nouvelles entités, vous devez réinitialiser tous les ID, de la classe et des largeurs de gamme. Ensuite, vous avez un clone "propre" et vous pouvez l'ajouter au DbSet (Ajouter, pas attacher).

using (var context = new MyContext(connection)) 
{ 
    context.Grades.Add(new Grade() 
    { 
     GradeWidths = new List<GradeWidth>(new[] 
     { 
      new GradeWidth() {Width = 10}, 
      new GradeWidth() {Width = 20}, 
      new GradeWidth() {Width = 30} 
     }) 
    }); 
    context.SaveChanges(); 
} 

using (var context = new MyContext(connection)) 
{ 
    Grade grade = context.Grades.Include(g => g.GradeWidths).AsNoTracking().First(); 

    // We need to reset all the ids 
    grade.Id = 0; 
    foreach (GradeWidth gradeWidth in grade.GradeWidths) 
     gradeWidth.Id = 0; 


    context.Grades.Add(grade); 
    context.SaveChanges(); 
} 

using (var context = new MyContext(connection)) 
{ 
    Debug.Assert(context.Grades.Count() == 2); 
    Debug.Assert(context.GradeWidths.Count() == 6); 
} 
+0

Je reçois une erreur 'La propriété 'GradeID' fait partie des informations clés de l'objet et ne peut pas être modifiée. Cela ne disparaîtra que si je détache l'objet mais que je perds la collection GradeWidths. – FloatingKiwi

+0

Dans mon cas cela fonctionne correctement et l'objet est détaché (avant de vous envoyer je cours le code). Probablement il y a un bug dans EF 5 dans asnotracking. Quoi qu'il en soit, si vous détachez l'objet principal que vous devez détacher, vous devez également les ajouter. – bubi