Entity Framework 4 - AddObject vs Attach

132

J'ai récemment travaillé avec Entity Framework 4 et je ne sais pas trop quand utiliser ObjectSet.Attach et ObjectSet.AddObject .

D'après ma compréhension:

  • Utilisez "Joindre" lorsqu'une entité existe déjà dans le système
  • Utilisez "AddObject" lors de la création d'une nouvelle entité

Donc, si je crée une nouvelle personne , je le fais.

var ctx = new MyEntities();
var newPerson = new Person { Name = "Joe Bloggs" };
ctx.Persons.AddObject(newPerson);
ctx.SaveChanges();

Si je modifie une personne existante , je fais ceci:

var ctx = new MyEntities();
var existingPerson = ctx.Persons.SingleOrDefault(p => p.Name = "Joe Bloggs" };
existingPerson.Name = "Joe Briggs";
ctx.SaveChanges();

Gardez à l'esprit qu'il s'agit d'un exemple très simple . En réalité, j'utilise Pure POCO (pas de génération de code), le modèle de référentiel (ne traite pas avec ctx.Persons) et Unit of Work (ne traite pas avec ctx.SaveChanges). Mais "sous les couvertures", ce qui précède est ce qui se passe dans ma mise en œuvre.

Maintenant, ma question - je n'ai pas encore trouvé de scénario dans lequel j'ai dû utiliser Attach .

Qu'est-ce que j'oublie ici? Quand devons-nous utiliser Attach?

ÉDITER

Juste pour clarifier, je cherche des exemples d'utilisation de Attach sur AddObject (ou vice-versa).

MODIFIER 2

La réponse ci-dessous est correcte (ce que j'ai accepté), mais j'ai pensé ajouter un autre exemple où Attach serait utile.

Dans mon exemple ci-dessus pour modifier une personne existante , deux requêtes sont en cours d'exécution.

Un pour récupérer la personne (.SingleOrDefault) et un autre pour effectuer la mise à jour (.SaveChanges).

Si (pour une raison quelconque), je savais déjà que "Joe Bloggs" existait dans le système, pourquoi faire une requête supplémentaire pour l'obtenir en premier? Je pourrais faire ceci:

var ctx = new MyEntities();
var existingPerson = new Person { Name = "Joe Bloggs" };
ctx.Persons.Attach(existingPerson);
ctx.SaveChanges();

Cela entraînera uniquement l'exécution d'une instruction UPDATE.

RPM1984
la source
Attach est également utilisé dans MVC de nos jours lors de la remise de modèles directement dans EF. Fonctionne plutôt bien et économise une tonne de lignes de code.
Piotr Kula

Réponses:

162

ObjectContext.AddObject et ObjectSet.AddObject :
laméthode AddObject permet d'ajouter des objets nouvellement créés qui n'existent pas dans la base de données. L'entité recevra une EntityKey temporaire générée automatiquementet son EntityState sera défini sur Ajouté . Lorsque SaveChanges est appelé, il sera clair pour l'EF que cette entité doit être insérée dans la base de données.

ObjectContext.Attach et ObjectSet.Attach :
D'autre part, Attach est utilisé pour les entités qui existent déjàdans la base de données. Plutôt que de définir EntityState sur Added, Attach aboutit à unEntityState inchangé , ce qui signifie qu'il n'a pas changé depuis qu'il a été attaché au contexte. Les objets que vous attachez sont supposés exister dans la base de données. Si vous modifiez les objets une fois qu'ils ont été attachés, lorsque vous appelez SaveChanges, la valeur de EntityKey est utilisée pour mettre à jour (ou supprimer) la ligne appropriée en recherchant son ID correspondant dans la table db.

De plus, à l'aide de la méthode Attach, vous pouvez définir des relations entre des entités qui existent déjà dans ObjectContext mais qui ontpas été connecté automatiquement. Fondamentalement, l'objectif principal de Attach est de connecter des entités qui sont déjà attachées à ObjectContext et qui ne sont pas nouvelles, vous ne pouvez donc pas utiliser Attach pour attacher des entités dont EntityState est ajouté. Vous devez utiliser Add () dans ce cas.

Par exemple, supposons que votre entité Person possède une propriété de navigation nommée Addresses qui est une collection d'entités Address . Supposons que vous ayez lu les deux objets à partir du contexte, mais qu'ils ne sont pas liés les uns aux autres et que vous souhaitez que ce soit le cas:

var existingPerson = ctx.Persons.SingleOrDefault(p => p.Name = "Joe Bloggs" };
var myAddress = ctx.Addresses.First(a => a.PersonID != existingPerson.PersonID);
existingPerson.Addresses.Attach(myAddress);
// OR:
myAddress.PersonReference.Attach(existingPerson)
ctx.SaveChanges();
Morteza Manavi
la source
Merci pour la réponse, je comprends la définition des deux (aka les deux premiers paragraphes). Mais je ne comprends pas un scénario où je DOIS utiliser Attach. Votre dernier paragraphe n'a pas vraiment de sens pour moi (se lit essentiellement comme une combinaison des deux premiers paragraphes), pouvez-vous me donner un exemple de l'endroit où j'utiliserais "Joindre" dans mon scénario ci-dessus? C'est vraiment ce que je recherche - des exemples, pas des définitions. J'apprécie vraiment votre temps. :)
RPM1984
1
Pas de problème, j'ai ajouté un extrait de code pour clarifier le dernier paragraphe, comme vous pouvez le voir, nous avons 2 objets non liés et Attach nous aide à les relier les uns aux autres. L'autre exemple serait d'utiliser la méthode Attach () pour attacher une «entité détachée» au contexte (il y a plusieurs raisons pour lesquelles vous pourriez vouloir qu'une entité détachée soit attachée au contexte)
Morteza Manavi
1
Oui, je l'ai maintenant. Je viens de regarder une vidéo TechEd sur EF4 (par Julie Lerman), qui a montré un exemple. Vous pouvez avoir une entité que vous N'AVEZ PAS récupérée d'une requête (c'est-à-dire qu'elle est déconnectée), mais vous savez qu'elle existe, vous utilisez donc Attach pour effectuer une MISE À JOUR sur cette entité. Cela a du sens, même si j'ai encore du mal à imaginer un scénario où vous auriez une entité «déconnectée». Merci de votre aide.
RPM1984
1
Génial. Puis-je vous demander s'il vous plaît de partager le lien de la vidéo pour d'autres collègues développeurs qui pourraient arriver à lire cet article?
Morteza Manavi
3
Le lien ci-dessus partagé par RPM1984 est rompu, il est maintenant redirigé vers channel9.msdn.com/Events/TechEd/NorthAmerica/2010/DEV205 . Enjoy
Empilé
31

C'est une réponse tardive, mais cela pourrait aider d'autres personnes qui le trouvent.

Fondamentalement, une entité "déconnectée" peut se produire lorsque vous manipulez une entité en dehors de la portée "using".

Employee e = null;

using (var ctx = new MyModelContainer())
{
     e = ctx.Employees.SingleOrDefault(emp => emp .....);
}

using (var ctx2 = new MyModelContainer())
{
     e; // This entity instance is disconnected from ctx2
}

Si vous entrez une autre portée "using", alors la variable "e" sera déconnectée car elle appartient à la portée "using" précédente et puisque la portée "using" précédente est détruite, alors "e" est déconnecté.

C'est comme ça que je le comprends.

TchiYuan
la source
3
L'exemple de Tchi est un exemple excellent et simple - ouais, la variable Employee doit être déclarée à l'extérieur. essayez e.Address.Street en dehors de la portée et voyez une fenêtre contextuelle d'exception de référence nulle. Si vous attachez, l'application n'aura pas à revenir à la base de données pour l'employé dans la deuxième portée.
Steve
9

Ceci est une citation de Programming Entity Framework: DbContext

L'appel de Remove sur une entité qui n'est pas suivie par le contexte provoquera la levée d'une InvalidOperationException. L'Entity Framework lève cette exception car il n'est pas clair si l'entité que vous essayez de supprimer est une entité existante qui doit être marquée pour suppression ou une nouvelle entité qui doit simplement être ignorée. Pour cette raison, nous ne pouvons pas utiliser simplement Supprimer pour marquer une entité déconnectée comme Supprimée; nous devons d'abord l'attacher .

private static void TestDeleteDestination()
{
    Destination canyon;
    using (var context = new BreakAwayContext())
    {
        canyon = (from d in context.Destinations
        where d.Name == "Grand Canyon"
        select d).Single();
    }
    DeleteDestination(canyon);
}
private static void DeleteDestination(Destination destination)
{
    using (var context = new BreakAwayContext())
    {
        context.Destinations.Attach(destination);
        context.Destinations.Remove(destination);
        context.SaveChanges();
    }
}

La méthode TestDeleteDestination simule une application client qui récupère une destination existante sur le serveur, puis la transmet à la méthode DeleteDestination sur le serveur. La méthode DeleteDestination utilise la méthode Attach pour indiquer au contexte qu'il s'agit d'une destination existante. Ensuite, la méthode Remove est utilisée pour enregistrer la destination existante pour la suppression

Teoman Shipahi
la source
-8

Qu'en est-il de se référer uniquement à la clé primaire au lieu de l'attacher?

c'est à dire:

var existingPerson = ctx.Persons.SingleOrDefault(p => p.Name = "Joe Bloggs" };
var myAddress = ctx.Addresses.First(a => a.PersonID != existingPerson.PersonID);
existingPerson.AddressId = myAddress.Id // not -> existingPerson.Addresses.Attach(myAddress);
// OR:
myAddress.Person.Id = existingPerson.Id // not -> myAddress.PersonReference.Attach(existingPerson);
ctx.SaveChanges();
Dan
la source