Lorsque je suis dans un scénario détaché et que je reçois un dto du client que je mappe dans une entité pour l'enregistrer, je fais ceci:
context.Entry(entity).State = EntityState.Modified;
context.SaveChanges();
Car quel est alors le DbSet.Attach(entity)
ou pourquoi devrais-je utiliser la méthode .Attach lorsque EntityState.Modified attache déjà l'entité?
c#
entity-framework
entity-framework-6
Élisabeth
la source
la source
Réponses:
Lorsque vous le faites
context.Entry(entity).State = EntityState.Modified;
, vous n'attachez pas seulement l'entité auDbContext
, vous marquez également l'entité entière comme sale. Cela signifie que lorsque vous le faitescontext.SaveChanges()
, EF générera une instruction de mise à jour qui mettra à jour tous les champs de l'entité.Ce n'est pas toujours souhaité.
D'autre part,
DbSet.Attach(entity)
attache l'entité au contexte sans la marquer comme sale. C'est équivalent à fairecontext.Entry(entity).State = EntityState.Unchanged;
Lors de l'attachement de cette manière, à moins que vous ne procédiez ensuite à la mise à jour d'une propriété sur l'entité, la prochaine fois que vous appelez
context.SaveChanges()
, EF ne générera pas de mise à jour de base de données pour cette entité.Même si vous prévoyez de mettre à jour une entité, si l'entité a beaucoup de propriétés (colonnes de base de données) mais que vous ne souhaitez en mettre à jour que quelques-unes, vous trouverez peut-être avantageux de faire une
DbSet.Attach(entity)
, puis de mettre à jour uniquement les quelques propriétés qui ont besoin de mise à jour. Cela générera une instruction de mise à jour plus efficace à partir d'EF. EF ne mettra à jour que les propriétés que vous avez modifiées (contrairement àcontext.Entry(entity).State = EntityState.Modified;
ce qui entraînera la mise à jour de toutes les propriétés / colonnes)Documentation pertinente: Ajouter / Joindre et États d'entité .
Exemple de code
Disons que vous avez l'entité suivante:
Si votre code ressemble à ceci:
Le SQL généré ressemblera à ceci:
Remarquez comment l'instruction de mise à jour ci-dessus mettra à jour toutes les colonnes, que vous ayez réellement modifié les valeurs ou non.
En revanche, si votre code utilise l'attachement "normal" comme ceci:
Ensuite, l'instruction de mise à jour générée est différente:
Comme vous pouvez le voir, l'instruction de mise à jour ne met à jour que les valeurs qui ont été réellement modifiées après avoir attaché l'entité au contexte. Selon la structure de votre table, cela peut avoir un impact positif sur les performances.
Maintenant, quelle option est la meilleure pour vous dépend entièrement de ce que vous essayez de faire.
la source
WHERE
clause contenant uniquement la clé primaire, et sans aucun contrôle de concurrence. Pour avoir la vérification de la concurrence, je dois configurer explicitement une colonne en tant que jeton de concurrence ou rowVersion. Dans ce cas, laWHERE
clause n'aura que la clé primaire et la colonne de jeton d'accès concurrentiel, pas tous les champs. Si vos tests montrent le contraire, j'aimerais en entendre parler.DbContext.Entry(person).CurrentValues
etDbContext.Entry(person).OriginalValues
.Lorsque vous utilisez la
DbSet.Update
méthode, Entity Framework marque toutes les propriétés de votre entité commeEntityState.Modified
, afin de les suivre. Si vous souhaitez modifier uniquement certaines de vos propriétés, pas toutes, utilisezDbSet.Attach
. Cette méthode crée toutes vos propriétésEntityState.Unchanged
, vous devez donc définir vos propriétés que vous souhaitez mettre à jourEntityState.Modified
. Ainsi, lorsque l'application atteintDbContext.SaveChanges
, elle n'opérera que les propriétés modifiées.la source
En plus (à la réponse marquée), il existe une différence importante entre
context.Entry(entity).State = EntityState.Unchanged
etcontext.Attach(entity)
(dans EF Core):J'ai fait quelques tests pour le comprendre plus par moi-même (cela inclut donc également des tests de référence généraux), voici donc mon scénario de test:
QueryTrackingBehavior.NoTracking
Voici les modèles:
Voici les données de test (d'origine) dans la base de données:
Pour obtenir la commande:
Maintenant les tests:
Mise à jour simple avec EntityState :
Mise à jour simple avec Attach :
Mise à jour avec modification des identifiants enfants avec EntityState :
Mise à jour avec modification des identifiants enfants avec Attach :
Remarque: cela génère une exception, peu importe si l'ID a été modifié ou a été défini sur la valeur d'origine, il semble que l'état de l'ID soit défini sur «modifié» et ce n'est pas autorisé (car c'est la clé primaire)
Mise à jour avec modification des identifiants enfants comme nouveaux (aucune différence entre EntityState et Attach):
Remarque: voyez la différence avec la mise à jour avec EntityState sans nouveau (ci-dessus). Cette fois, le nom sera mis à jour, en raison de la nouvelle instance utilisateur.
Mettre à jour avec la modification des ID de référence avec EntityState :
Mettre à jour avec la modification des ID de référence avec Attach :
Remarque: la référence sera changée en utilisateur 3, mais aussi l'utilisateur 1 sera mis à jour, je suppose que c'est parce que le
order.OrderedByUser.Id
est inchangé (c'est toujours 1).Conclusion Avec EntityState, vous avez plus de contrôle, mais vous devez mettre à jour les sous-propriétés (de deuxième niveau) par vous-même. Avec Attach, vous pouvez tout mettre à jour (je suppose avec tous les niveaux de propriétés), mais vous devez garder un œil sur les références. Juste par exemple: si User (OrderedByUser) était un dropDown, changer la valeur via un dropDown pourrait écraser tout l'objet User. Dans ce cas, la valeur dropDown d'origine serait écrasée à la place de la référence.
Pour moi, le meilleur des cas est de définir des objets comme OrderedByUser sur null et de définir uniquement order.OrderedByUserId sur la nouvelle valeur, si je veux uniquement modifier la référence (peu importe si EntityState ou Attach).
J'espère que cela aide, je sais que c'est beaucoup de texte: D
la source