Mettre à jour un enregistrement sans interrogation préalable?

104

Disons que j'interroge la base de données et charge une liste d'éléments. Ensuite, j'ouvre l'un des éléments dans un formulaire de vue détaillée et au lieu de réinterroger l'élément hors de la base de données, je crée une instance de l'élément à partir de la source de données dans la liste.

Existe-t-il un moyen de mettre à jour l'enregistrement de la base de données sans récupérer l'enregistrement de l'élément individuel?

Voici un exemple de comment je le fais maintenant:

dataItem itemToUpdate = (from t in dataEntity.items
                                 where t.id == id
                                 select t).FirstOrDefault();

Ensuite, après avoir extrait l'enregistrement, je mets à jour certaines valeurs de l'élément et repousse l'enregistrement:

itemToUpdate.itemstatus = newStatus;
dataEntity.SaveChanges();

Je pense qu'il y aurait une meilleure façon de faire cela, des idées?

Shane Grant
la source
2
Ce n'est pas une très mauvaise façon de faire les choses. Avez-vous un accès simultané à cette table?
Henk Holterman
Je pense que c'est l'usage qu'un ORM comme EF est exactement là pour servir. Pour permettre aux opérations dans le contexte de l'application d'être effectuées sur les objets que vous souhaitez créer / modifier / supprimer, sans vous soucier de l'implémentation DB sous-jacente?
Pero P.
40
Je pense que pour les développeurs ayant une formation en TSQL essayant d'accepter et d'adopter les ORM, il est un peu inefficace de rechercher un enregistrement uniquement pour le mettre à jour et de ne jamais utiliser les données récupérées. Ce concept selon lequel un développeur n'a pas besoin de se préoccuper de l'implémentation de base de données sous-jacente est un crock. Plus un développeur en sait sur l'ensemble du système, meilleure sera la solution. Les options ne sont jamais une mauvaise chose.
barrypicker
1
L'approche ORM convient parfaitement aux objets réels, mais si vous stockez également d'autres éléments dans votre base de données (comme de gros blobs binaires), il peut être très utile de pouvoir les mettre à jour sans charger d'abord le contenu d'origine.
BrainSlugs83

Réponses:

68

Vous devez utiliser la méthode Attach () .

Attacher et détacher des objets

CD..
la source
16
Pouvez vous donner un exemple?
Bart Calixto
17
context.Products.Attach (produit); context.Entry (produit) .State = EntityState.Modified;
Gabriel
6
@Gabriel Cela ne mettra-t-il pas à jour toutes les propriétés? Et si je ne veux en modifier qu'un seul?
David Pfeffer
22
Oui, cela mettra à jour toutes les propriétés. Si vous souhaitez mettre à jour une seule propriété, vous pouvez le faire: context.Entry (utilisateur) .Property (x => x.Property) .IsModified = true; (jetez un œil ici stackoverflow.com/a/5567616/57369 )
Gabriel
6
Je voudrais juste ajouter ce contexte.Entry () n'est disponible que dans .net 4.1, si vous utilisez toujours 4.0 (comme moi), vérifiez ceci pour l'alternative: stackoverflow.com/questions/7113434/where-is- entrée de contexte qui est essentiellement: context.ObjectStateManager.ChangeObjectState (yourObject, EntityState.Modified);
dyslexicanaboko
39

Vous pouvez également utiliser SQL direct sur la base de données en utilisant le contexte de la banque de données. Exemple:

dataEntity.ExecuteStoreCommand
   ("UPDATE items SET itemstatus = 'some status' WHERE id = 123 ");

Pour des raisons de performances, vous souhaiterez peut-être transmettre des variables au lieu d'une seule chaîne SQL codée en dur. Cela permettra à SQL Server de mettre en cache la requête et de la réutiliser avec des paramètres. Exemple:

dataEntity.ExecuteStoreCommand
   ("UPDATE items SET itemstatus = 'some status' WHERE id = {0}", new object[] { 123 });

UPDATE - pour EF 6.0

dataEntity.Database.ExecuteSqlCommand
       ("UPDATE items SET itemstatus = 'some status' WHERE id = {0}", new object[] { 123 });
barrypicker
la source
9
pourquoi voudriez-vous déclasser cette réponse sans laisser de commentaire. Cette suggestion répond parfaitement à la question des auteurs originaux.
barrypicker
18
ExecuteStoreCommandn'est pas vraiment un moyen EF de faire cela, il utilise simplement le DbConnectioncontenu dans le DbContextpour exécuter une commande. Ce n'est pas indépendant de la base de données, encore moins de la persistance (par exemple, cet exemple planterait si l'OP passait à XML).
just.another.programmer
9
@ just.another.programmer - avec une grande puissance vient une grande responsabilité.
barrypicker
13
Doit-il être indépendant de la persistance? Ce n'est pas comme si vous changiez votre système de stockage tous les deux jours.
David
5
@ BrainSlugs83 - essayez d'utiliser EF sur des serveurs de liens qui ne prennent en charge que OpenQuery - beaucoup de plaisir. Parfois, vous avez absolument besoin de SQL brut pour faire le travail. Vous ne pouvez pas toujours dessiner le code en isolement pour le tester. Ce n'est pas un monde parfait là-bas.
barrypicker
20

Le code:

ExampleEntity exampleEntity = dbcontext.ExampleEntities.Attach(new ExampleEntity { Id = 1 });
exampleEntity.ExampleProperty = "abc";
dbcontext.Entry<ExampleEntity>(exampleEntity).Property(ee => ee.ExampleProperty).IsModified = true;
dbcontext.Configuration.ValidateOnSaveEnabled = false;
dbcontext.SaveChanges();

Le résultat TSQL:

exec sp_executesql N'UPDATE [dbo].[ExampleEntities]
SET [ExampleProperty ] = @0
WHERE ([Id] = @1)
',N'@0 nvarchar(32),@1 bigint',@0='abc',@1=1

Remarque:

La ligne "IsModified = true" est nécessaire car lorsque vous créez le nouvel objet ExampleEntity (uniquement avec la propriété Id remplie), toutes les autres propriétés ont leurs valeurs par défaut (0, null, etc.). Si vous souhaitez mettre à jour la base de données avec une «valeur par défaut», la modification ne sera pas détectée par la structure d'entité et la base de données ne sera pas mise à jour.

Par exemple:

exampleEntity.ExampleProperty = null;

ne fonctionnera pas sans la ligne "IsModified = true", car la propriété ExampleProperty, est déjà nulle lorsque vous avez créé l'objet ExampleEntity vide, vous devez dire à EF que cette colonne doit être mise à jour, et c'est le but de cette ligne.

tecla
la source
C'est parfait. Je viens de tester cela et c'est exactement ce que je voulais. Je souhaite que les modifications passent par l'infrastructure EF (y compris en utilisant le projet EntityFramework.Triggers) mais je voulais pouvoir modifier 1 colonne en ne disposant que de la clé primaire.
MikeJansen
11

Si les DataItemchamps has EF seront pré-validés (comme les champs non nullables), nous devrons désactiver cette validation pour ce contexte:

DataItem itemToUpdate = new DataItem { Id = id, Itemstatus = newStatus };
dataEntity.Entry(itemToUpdate).Property(x => x.Itemstatus).IsModified = true;
dataEntity.Configuration.ValidateOnSaveEnabled = false;
dataEntity.SaveChanges();
//dataEntity.Configuration.ValidateOnSaveEnabled = true;

Sinon, nous pouvons essayer de satisfaire la pré-validation et ne mettre à jour que la seule colonne:

DataItem itemToUpdate = new DataItem
{
    Id = id,
    Itemstatus = newStatus,
    NonNullableColumn = "this value is disregarded - the db original will remain"
};
dataEntity.Entry(itemToUpdate).Property(x => x.Itemstatus).IsModified = true;
dataEntity.SaveChanges();

En supposant que dataEntityc'est unSystem.Data.Entity.DbContext

Vous pouvez vérifier la requête générée en ajoutant ceci à DbContext:

/*dataEntity.*/Database.Log = m => System.Diagnostics.Debug.Write(m);
Aske B.
la source
0

Cela fonctionne quelque peu différemment dans EF Core:

Il peut y avoir un moyen plus rapide de le faire dans EF Core, mais ce qui suit garantit une mise à jour sans avoir à faire un SELECT (testé avec EF Core 2 et JET sur le .NET Framework 4.6.2):

Assurez-vous que votre modèle n'a pas de propriétés IsRequired

Ensuite, utilisez le modèle suivant (dans VB.NET):

    Using dbContext = new MyContext()
        Dim bewegung = dbContext.MyTable.Attach(New MyTable())
        bewegung.Entity.myKey = someKey
        bewegung.Entity.myOtherField = "1"

        dbContext.Entry(bewegung.Entity).State = EntityState.Modified
        dbContext.Update(bewegung.Entity)

        Dim BewegungenDescription = (From tp In dbContext.Model.GetEntityTypes() Where tp.ClrType.Name = "MyTable" Select tp).First()
        For Each p In (From prop In BewegungenDescription.GetProperties() Select prop)
            Dim pp = dbContext.Entry(bewegung.Entity).Property(p.Name)
            pp.IsModified = False
        Next
        dbContext.Entry(bewegung.Entity).Property(Function(row) row.myOtherField).IsModified = True
        dbContext.SaveChanges()
    End Using
Wolfgang Grinfeld
la source
-1

De manière générale, si vous avez utilisé Entity Framework pour interroger tous les éléments et que vous avez enregistré l'objet entité, vous pouvez mettre à jour les éléments individuels dans l'objet entité et appeler SaveChanges()lorsque vous avez terminé. Par exemple:

var items = dataEntity.Include("items").items;
// For each one you want to change:
items.First(item => item.id == theIdYouWant).itemstatus = newStatus;
// After all changes:
dataEntity.SaveChanges();

La récupération de l'élément souhaité ne doit pas générer de nouvelle requête.

Andrew
la source
Réponse intéressante, quelqu'un d'autre l'a-t-il confirmé?
Ian
5
Cela fait la même chose que le problème d'OP: récupérer l'intégralité de l'enregistrement, puis le mettre à jour. .First () désérialise l'objet.
Jerther