2014-09-08 6 views
20

Dans mon programme MVVM, j'ai une classe Model (disons MyModel) à partir de laquelle j'ai une instance de lecture de la base de données (en utilisant Entity Framework). Lors de la récupération de l'objet, je présente toutes les données à l'utilisateur. Plus tard, l'utilisateur va modifier certains champs.
Ce que je veux est de créer le même objet, sauf pour c'est ID (depuis que ID est l'auto clé primaire et incrémentée).
Alors, comment pourrais-je aborder cela? Je ne veux pas copier tous les champs un par un, ce n'est pas une approche robuste. Parce que peut-être que dans le futur le modèle pourra être modifié, ainsi je devrai en tenir compte dans la méthode de clonage.Entity Framework 6: Objet clone sauf ID

Y a-t-il une façon élégante de copier l'objet et lors de l'enregistrement dans la base de données, son ID est automatiquement incrémenté à nouveau? (Définir l'ID à null me donne une erreur de compilation, car il est de type int).

+0

Qu'en est-il de ValueInjecter? Malheureusement, il fait une copie superficielle, pas profonde. http://valueinjecter.codeplex.com –

+1

Pouvez-vous nous montrer votre classe de modèle? Si non, est-ce un POCO? A-t-il des propriétés complexes? –

+1

juste en train de terminer la lecture, avant de penser, je dirais, charger puis détacher et vous aurez votre copie prête pour l'insertion. Maintenant, nous devons parler de type complexe comme suggéré par @GeorgeVovos – tschmit007

Répondre

26

J'ai remarqué qu'il n'y a pas besoin de copier. Apparemment, lors de l'ajout d'une instance d'un modèle à la base de données (même si l'ID est déjà défini dans la base de données), Entity Framework insère une nouvelle ligne dans la base de données et incrémente automatiquement sa clé primaire. Donc, cette fonctionnalité est déjà intégrée dans EF. Je ne le savais pas, désolé.
Juste pour des raisons de clarté est un exemple ici:

using(var database = new MyDbContext()) { 
    MyModel myModel = database.Where(m => m.SomeProperty == someValue); 
    myModel.SomeOtherProperty = someOtherValue; //user changed a value 
    database.MyModels.Add(myModel); //even though the ID of myModel exists in the database, it gets added as a new row and the ID gets auto-incremented 
    database.SaveChanges(); 
} 
+1

Parfois, c'est un peu trop facile. J'avais essayé une heure avec MemberwiseClone ... – Niklas

+0

** Où ** renvoie une collection si. Cela ne fonctionnera pas non plus si votre entité possède un objet ou une collection d'objets. Au moins pas pour moi. –

+3

@THEStephenStanton La méthode d'extension '.Where()' linq n'est qu'un exemple. Vous pouvez également utiliser la méthode '.Find()' sur un Entity Framework 'DbSet ' pour cela. Mais ce n'est pas le point, le fait est que EF auto-incrémente l'ID lorsque vous ajoutez un modèle sale à votre DbContext. – QuantumHive

4

Lorsque vous utilisez ObjectContext la réponse fournie par QuantumHive ne fonctionne pas.

L'erreur renvoyée dans cette situation est la suivante:

An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key. 
System.InvalidOperationException: An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key. 
    at System.Data.Objects.ObjectStateManager.AddEntry(IEntityWrapper wrappedObject, EntityKey passedKey, EntitySet entitySet, String argumentName, Boolean isAdded) 
    at System.Data.Objects.ObjectContext.AddSingleObject(EntitySet entitySet, IEntityWrapper wrappedEntity, String argumentName) 
    at System.Data.Objects.DataClasses.RelatedEnd.AddEntityToObjectStateManager(IEntityWrapper wrappedEntity, Boolean doAttach) 
    at System.Data.Objects.DataClasses.RelatedEnd.AddGraphToObjectStateManager(IEntityWrapper wrappedEntity, Boolean relationshipAlreadyExists, Boolean addRelationshipAsUnchanged, Boolean doAttach) 
    at System.Data.Objects.DataClasses.RelatedEnd.Add(IEntityWrapper wrappedTarget, Boolean applyConstraints, Boolean addRelationshipAsUnchanged, Boolean relationshipAlreadyExists, Boolean allowModifyingOtherEndOfRelationship, Boolean forceForeignKeyChanges) 
    at System.Data.Objects.DataClasses.RelatedEnd.Add(IEntityWrapper wrappedEntity, Boolean applyConstraints) 
    at System.Data.Objects.DataClasses.EntityReference`1.set_ReferenceValue(IEntityWrapper value) 
    at System.Data.Objects.DataClasses.EntityReference`1.set_Value(TEntity value) 

Pour cloner correctement un objet-cadre de l'entité (au moins en EF6.0) est:

/// <summary> 
/// Clone a replica of this item in the database 
/// </summary> 
/// <returns>The cloned item</returns> 
public Item CloneDeep() 
{ 
    using (var context = new EntityObjectContext()) 
    { 
     var item = context.Items 
      .Where(i => i.ItemID == this.ItemID) 
      .Single(); 
     context.Detach(item); 
     item.EntityKey = null; 
     item.ItemID = 0; 
     return item; 
    } 
} 
+0

Cela dépend de votre contexte, si vous n'appelez pas ['context.SaveChanges()'] (http://www.entityframeworktutorial.net/significance-of-savechanges.aspx) avant d'insérer une clé en double, puis le sous-jacent ci-joint Les entités peuvent avoir des clés en double, et c'est ce à quoi le message d'exception se plaint légitimement. Cela dépend donc totalement de votre cas d'utilisation et de votre implémentation. Vous ne devriez pas ** avoir ** des entités en double avec la même clé attachée au même contexte. Dans mon exemple, je suis évidemment en train d'interroger une entité à partir d'un nouveau contexte de base de données, donc dans ce cas il * fonctionne *. – QuantumHive

0

Je trouve cette recherche pour voir si Il y avait une meilleure façon de cloner un objet que j'utilisais actuellement et j'ai remarqué qu'il y avait un problème potentiel avec la réponse acceptée si vous essayez de faire plusieurs clones ... au moins si vous voulez éviter de créer votre contexte plusieurs fois. ..

Je ne sais pas si c'est la meilleure approche pour le clonage, c'est pourquoi je cherchais un autre moyen. Mais ça fonctionne. Si vous devez cloner une entité plusieurs fois, vous pouvez utiliser la sérialisation JSON pour cloner ... quelque chose comme ça (en utilisant Newtonsoft JSON).

using(var context = new Context()) { 
    Link link = context.Links.Where(x => x.Id == someId); 
    bool isFirst = true; 
    foreach(var id in userIds) { 
     if(isFirst) { 
      link.UserId = id; 
      isFirst  = false; 
     } 
     else { 
      string cloneString = JsonConvert.SerializeObject(link); 
      Link clone = JsonConvert.DeserializeObject<Link>(cloneString); 
      clone.UserId = id; 
      context.Links.Add(clone); 
     } 
    } 
    context.SaveChanges(); 
} 
17

Lori Peterson a suggéré d'utiliser .AsNoTracking() pour effectuer le clonage dans EF6. J'utilise cette méthode et peux confirmer que cela fonctionne. Vous pouvez même inclure des objets enfants.

var entity = context.Entities 
        .AsNoTracking() 
        .Include(x => x.ChildEntities) 
        .FirstOrDefault(x => x.EntityId == entityId); 

entity.SomeProperty = DateTime.Now; 

context.Entities.Add(entity); 
context.SaveChanges(); 

Lorsque vous récupérez une entité ou des entités d'un ensemble de données, vous pouvez dire Entity Framework de ne pas suivre l'une des modifications que vous apportez à cet objet, puis ajoutez cette entité comme une nouvelle entité l'ensemble de données. Avec l'utilisation de .AsNoTracking, le contexte ne sait rien de l'entité existante.

+0

J'ai utilisé cette approche, mais j'ai dû modifier la section Include à '.Include (" ChildEntities ")' - à partir de msdn: liste d'objets liés séparés par des points à renvoyer dans les résultats de la requête. –

+0

J'utilise cette méthode, cependant, quand j'ajoute l'entité au contexte qu'elle se plaint d'une contrainte de multiplicité, c'est comme si l'entité enfant n'incrémente pas sa clé primaire. Est-ce que quelqu'un d'autre a eu ce problème et sait comment le résoudre? – Dalton

+0

@Dalton Je suggérerais d'entrer une nouvelle question et d'inclure le code pertinent. Je ne suis pas sûr de ce qui causerait le problème que vous mentionnez. – jaycer

Questions connexes