Aggiornare un record senza prima interrogare?


104

Supponiamo che interroghi il database e carichi un elenco di elementi. Quindi apro uno degli elementi in un modulo di visualizzazione dei dettagli e, invece di interrogare nuovamente l'elemento fuori dal database, creo un'istanza dell'elemento dall'origine dati nell'elenco.

C'è un modo per aggiornare il record del database senza recuperare il record del singolo elemento?

Ecco un esempio di come lo sto facendo ora:

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

Quindi, dopo aver estratto il record, aggiorno alcuni valori nell'elemento e spingo indietro il record:

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

Penso che ci sarebbe un modo migliore per farlo, qualche idea?


2
Non è un modo terribilmente brutto di fare le cose. Hai accesso simultaneo a quella tabella?
Henk Holterman

Penso che questo sia l'uso che un ORM come EF è esattamente lì per servire. Per consentire di eseguire operazioni all'interno del contesto dell'applicazione sugli oggetti che si desidera creare / modificare / eliminare, senza preoccuparsi per l'implementazione del DB sottostante?
Pero P.

40
Penso che per gli sviluppatori con un background in TSQL che cercano di accettare e abbracciare gli ORM, è un po 'inefficiente cercare un record solo per aggiornarlo e non utilizzare mai i dati recuperati. Questo concetto secondo cui uno sviluppatore non deve preoccuparsi dell'implementazione del database sottostante è un errore. Più uno sviluppatore conosce l'intero sistema, migliore può essere la soluzione. Le opzioni non sono mai una cosa negativa.
barrypicker

1
L'approccio ORM va bene per gli oggetti reali, ma se memorizzi anche altre cose nel tuo database (come i grandi blob binari) può essere molto utile essere in grado di aggiornarli senza caricare prima il contenuto originale.
BrainSlugs83

Risposte:


68

16
puoi fornire un esempio?
Bart Calixto

17
context.Products.Attach (prodotto); context.Entry (prodotto) .State = EntityState.Modified;
Gabriel

6
@ Gabriel non aggiornerà però tutte le proprietà? E se ne volessi modificare solo uno?
David Pfeffer

22
Sì, questo aggiornerà tutte le proprietà. Se vuoi aggiornare una singola proprietà puoi farlo: context.Entry (user) .Property (x => x.Property) .IsModified = true; (dai un'occhiata qui stackoverflow.com/a/5567616/57369 )
Gabriel

6
Vorrei solo aggiungere quel context.Entry () è disponibile solo in .net 4.1, se stai ancora utilizzando 4.0 (come me), controlla questo per l'alternativa: stackoverflow.com/questions/7113434/where-is- voce di contesto che è essenzialmente: context.ObjectStateManager.ChangeObjectState (yourObject, EntityState.Modified);
dyslexicanaboko

39

È inoltre possibile utilizzare l'SQL diretto sul database utilizzando il contesto del datastore. Esempio:

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

Per motivi di prestazioni, potresti voler passare le variabili invece di una singola stringa SQL hardcoded. Ciò consentirà a SQL Server di memorizzare nella cache la query e di riutilizzarla con i parametri. Esempio:

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

AGGIORNAMENTO - per EF 6.0

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

9
perché dovresti eseguire il downgrade di questa risposta senza lasciare un commento. Questo suggerimento risponde alla domanda dell'autore originale.
barrypicker

18
ExecuteStoreCommandnon è davvero un modo EF di farlo, sta solo usando il DbConnectioncontenuto all'interno di DbContextper eseguire un comando. Non è indipendente dal database, per non parlare della persistenza (ad esempio, questo esempio andrebbe in crash se l'OP passasse a XML).
solo.un altro.programmatore

9
@ just.another.programmer - da un grande potere derivano grandi responsabilità.
barrypicker

13
Deve essere agnostico per la persistenza? Non è che cambierai il tuo sistema di archiviazione a giorni alterni.
David

5
@ BrainSlugs83 - prova a utilizzare EF su server di collegamento che supportano solo OpenQuery - molto divertente. A volte hai assolutamente bisogno di SQL grezzo per portare a termine il lavoro. Non sempre è possibile isolare il codice per il test. Non è un mondo perfetto là fuori.
barrypicker

20

Il codice:

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();

Il risultato TSQL:

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

Nota:

La riga "IsModified = true" è necessaria perché quando crei il nuovo oggetto ExampleEntity (solo con la proprietà Id popolata) tutte le altre proprietà hanno i loro valori di default (0, null, ecc.). Se desideri aggiornare il DB con un "valore predefinito", la modifica non verrà rilevata dal framework dell'entità e quindi il DB non verrà aggiornato.

Per esempio:

exampleEntity.ExampleProperty = null;

non funzionerà senza la riga "IsModified = true", perché la proprietà ExampleProperty, è già nulla quando hai creato l'oggetto ExampleEntity vuoto, devi dire a EF che questa colonna deve essere aggiornata, e questo è lo scopo di questa riga.


Questo è perfetto. L'ho appena provato ed è esattamente quello che volevo. Voglio che le modifiche passino attraverso l'infrastruttura EF (incluso l'utilizzo del progetto EntityFramework.Triggers) ma volevo essere in grado di modificare 1 colonna con solo la chiave primaria.
MikeJansen

11

Se DataItemEF ha campi pre-convaliderà (come i campi non annullabili), dovremo disabilitare tale convalida per questo contesto:

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;

Altrimenti possiamo provare a soddisfare la pre-convalida e comunque aggiornare solo la singola colonna:

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();

Supponendo che dataEntitysia un fileSystem.Data.Entity.DbContext

Puoi verificare la query generata aggiungendola a DbContext:

/*dataEntity.*/Database.Log = m => System.Diagnostics.Debug.Write(m);


0

Funziona in modo leggermente diverso in EF Core:

Potrebbe esserci un modo più veloce per eseguire questa operazione in EF Core, ma quanto segue garantisce un UPDATE senza dover eseguire un SELECT (testato con EF Core 2 e JET in .NET Framework 4.6.2):

Assicurati che il tuo modello non abbia proprietà IsRequired

Quindi utilizzare il seguente modello (in 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

-1

In generale, se hai usato Entity Framework per interrogare tutti gli elementi e hai salvato l'oggetto entità, puoi aggiornare i singoli elementi nell'oggetto entità e chiamare SaveChanges()quando hai finito. Per esempio:

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();

Il recupero dell'elemento desiderato non dovrebbe generare una nuova query.


Risposta interessante, qualcun altro l'ha confermato?
Ian

5
Questo fa la stessa cosa del problema di OP: recupera l'intero record, quindi aggiornalo. .First () deserializza l'oggetto.
Jerther
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.