Einen Datensatz aktualisieren, ohne vorher abzufragen?

102

Nehmen wir an, ich frage die Datenbank ab und lade eine Liste mit Elementen. Dann öffne ich eines der Elemente in einer Detailansicht und anstatt das Element erneut aus der Datenbank abzufragen, erstelle ich eine Instanz des Elements aus der Datenquelle in der Liste.

Gibt es eine Möglichkeit, den Datenbankeintrag zu aktualisieren, ohne den Datensatz des einzelnen Elements abzurufen?

Hier ist ein Beispiel, wie ich es jetzt mache:

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

Nachdem ich den Datensatz abgerufen habe, aktualisiere ich einige Werte im Element und schiebe den Datensatz zurück:

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

Ich würde denken, es gäbe einen besseren Weg, dies zu tun, irgendwelche Ideen?

Shane Grant
quelle
2
Es ist keine schrecklich schlechte Art, Dinge zu tun. Haben Sie gleichzeitig Zugriff auf diese Tabelle?
Henk Holterman
Ich würde denken, dass dies die Verwendung ist, für die ein ORM wie EF genau da ist. Damit Operationen im Kontext der Anwendung für die Objekte ausgeführt werden können, die Sie erstellen / ändern / löschen möchten, ohne die zugrunde liegende DB-Implementierung zu berücksichtigen?
Pero P.
39
Ich denke, für Entwickler mit TSQL-Hintergrund, die versuchen, ORMs zu akzeptieren und zu akzeptieren, ist es etwas ineffizient, einen Datensatz nur zum Aktualisieren zu suchen und die abgerufenen Daten niemals zu verwenden. Dieses Konzept, dass sich ein Entwickler nicht mit der zugrunde liegenden DB-Implementierung befassen muss, ist ein Trottel. Je mehr ein Entwickler über das gesamte System weiß, desto besser kann die Lösung sein. Optionen sind niemals eine schlechte Sache.
Barrypicker
1
Der ORM-Ansatz ist für tatsächliche Objekte in Ordnung, aber wenn Sie auch andere Dinge in Ihrer Datenbank speichern (wie große binäre Blobs), kann es sehr nützlich sein, sie aktualisieren zu können, ohne zuerst den ursprünglichen Inhalt zu laden.
BrainSlugs83

Antworten:

68

Sie sollten die Attach () -Methode verwenden.

Objekte anbringen und abnehmen

CD..
quelle
16
Können Sie ein Beispiel geben?
Bart Calixto
16
context.Products.Attach (Produkt); context.Entry (Produkt) .State = EntityState.Modified;
Gabriel
6
@ Gabriel Wird dieses Update nicht alle Eigenschaften aktualisieren? Was ist, wenn ich nur einen einzigen ändern möchte?
David Pfeffer
22
Ja, dadurch werden alle Eigenschaften aktualisiert. Wenn Sie eine einzelne Eigenschaft aktualisieren möchten, können Sie dies tun: context.Entry (Benutzer) .Property (x => x.Property) .IsModified = true; (Schauen Sie hier stackoverflow.com/a/5567616/57369 )
Gabriel
6
Ich möchte nur diesen Kontext hinzufügen. Entry () ist nur in .net 4.1 verfügbar. Wenn Sie noch 4.0 verwenden (wie ich), prüfen Sie dies für die Alternative: stackoverflow.com/questions/7113434/where-is- Kontexteintrag, der im Wesentlichen lautet: context.ObjectStateManager.ChangeObjectState (yourObject, EntityState.Modified);
Legasthenikeraboko
39

Sie können auch direktes SQL für die Datenbank im Kontext des Datenspeichers verwenden. Beispiel:

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

Aus Leistungsgründen möchten Sie möglicherweise Variablen anstelle einer einzelnen fest codierten SQL-Zeichenfolge übergeben. Dadurch kann SQL Server die Abfrage zwischenspeichern und mit Parametern wiederverwenden. Beispiel:

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

UPDATE - für EF 6.0

dataEntity.Database.ExecuteSqlCommand
       ("UPDATE items SET itemstatus = 'some status' WHERE id = {0}", new object[] { 123 });
Barrypicker
quelle
9
Warum sollten Sie diese Antwort herabstufen, ohne einen Kommentar zu hinterlassen? Dieser Vorschlag befasst sich mit der Frage des ursprünglichen Autors.
Barrypicker
18
ExecuteStoreCommandist nicht wirklich eine EF-Methode, sondern verwendet nur das darin DbConnectionenthaltene DbContext, um einen Befehl auszuführen. Es ist nicht datenbankunabhängig, geschweige denn persistenzunabhängig (z. B. würde dieses Beispiel abstürzen, wenn das OP auf XML umgestellt würde).
just.another.programmer
9
@ just.another.programmer - mit großer Kraft geht große Verantwortung einher.
Barrypicker
13
Muss es persistenzunabhängig sein? Es ist nicht so, dass Sie Ihr Speichersystem jeden zweiten Tag ändern werden.
David
5
@ BrainSlugs83 - versuchen Sie, EF auf Link-Servern zu verwenden, die nur OpenQuery unterstützen - viel Spaß. Manchmal benötigen Sie unbedingt unformatiertes SQL, um die Aufgabe zu erledigen. Nicht immer können Sie den Code zum Testen isoliert zeichnen. Es ist keine perfekte Welt da draußen.
Barrypicker
19

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

Das Ergebnis TSQL:

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

Hinweis:

Die Zeile "IsModified = true" wird benötigt, da beim Erstellen des neuen ExampleEntity-Objekts (nur mit der aufgefüllten Id-Eigenschaft) alle anderen Eigenschaften ihre Standardwerte (0, null usw.) haben. Wenn Sie die Datenbank mit einem "Standardwert" aktualisieren möchten, wird die Änderung vom Entitätsframework nicht erkannt, und die Datenbank wird nicht aktualisiert.

Zum Beispiel:

exampleEntity.ExampleProperty = null;

funktioniert nicht ohne die Zeile "IsModified = true", da die Eigenschaft ExampleProperty bereits null ist, als Sie das leere ExampleEntity-Objekt erstellt haben. Sie müssen EF mitteilen, dass diese Spalte aktualisiert werden muss, und dies ist der Zweck dieser Zeile.

Tecla
quelle
Dies ist perfekt. Ich habe das gerade getestet und es ist genau das, was ich wollte. Ich möchte, dass die Änderungen die EF-Infrastruktur durchlaufen (einschließlich der Verwendung des EntityFramework.Triggers-Projekts), möchte aber in der Lage sein, eine Spalte mit nur dem Primärschlüssel zu ändern.
MikeJansen
11

Wenn die DataItemEF-Felder EF vorab validieren (wie nicht nullbare Felder), müssen wir diese Validierung für diesen Kontext deaktivieren:

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;

Andernfalls können wir versuchen, die Vorvalidierung zu erfüllen und trotzdem nur die einzelne Spalte zu aktualisieren:

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

Angenommen, dataEntityist aSystem.Data.Entity.DbContext

Sie können die generierte Abfrage überprüfen, indem Sie Folgendes hinzufügen DbContext:

/*dataEntity.*/Database.Log = m => System.Diagnostics.Debug.Write(m);
Fragen Sie B.
quelle
0

In EF Core funktioniert das etwas anders:

In EF Core gibt es möglicherweise eine schnellere Möglichkeit, dies zu tun. Im Folgenden wird jedoch ein UPDATE sichergestellt, ohne dass ein SELECT durchgeführt werden muss (getestet mit EF Core 2 und JET unter .NET Framework 4.6.2):

Stellen Sie sicher, dass Ihr Modell keine IsRequired-Eigenschaften hat

Verwenden Sie dann die folgende Vorlage (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
Wolfgang Grinfeld
quelle
-1

Wenn Sie Entity Framework zum Abfragen aller Elemente verwendet und das Entitätsobjekt gespeichert haben, können Sie im Allgemeinen die einzelnen Elemente im Entitätsobjekt aktualisieren und aufrufen, SaveChanges()wenn Sie fertig sind. Beispielsweise:

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

Das Abrufen des gewünschten Elements sollte keine neue Abfrage generieren.

Andrew
quelle
Interessante Antwort, hat das noch jemand bestätigt?
Ian
5
Dies entspricht dem Problem von OP: Den gesamten Datensatz abrufen und dann aktualisieren. .First () deserialisiert das Objekt.
Jerther