Update-Zeile, wenn es andere gibt Einfügen von Logik mit Entity Framework


125

Hat jemand Vorschläge für die effizienteste Möglichkeit zu implementieren "Zeile aktualisieren, wenn es sonst gibt einfügen" Logik mit Entity Framework?

+2

Dies ist etwas, das in der Datenbank-Engine-Ebene getan werden soll, in einer gespeicherten Prozedur. Andernfalls müssen Sie die Erkennung/Aktualisierung/Einfügung in eine Transaktion umbrechen. 06 apr. 112011-04-06 09:12:11

+1

@Stephen: Das ist tatsächlich, was ich getan habe. Vielen Dank. 06 apr. 112011-04-06 14:18:43

  0

Jonathan, deine Frage ist sehr nützlich für mich. Warum haben Sie zu einer gespeicherten Prozedur gewechselt? 15 jan. 142014-01-15 09:54:35

+2

@Anar: Es war einfach einfacher und ich erwarte viel effizienter. 15 jan. 142014-01-15 16:58:03

  0

Müssen Sie eine gespeicherte Prozedur für jede Tabelle schreiben? 03 jan. 182018-01-03 05:00:58

130

Wenn Sie mit angesetztem Objekt (Objekt aus der gleichen Instanz der Kontext geladen) arbeiten, können Sie einfach verwenden:

if (context.ObjectStateManager.GetObjectStateEntry(myEntity).State == EntityState.Detached) 
{ 
    context.MyEntities.AddObject(myEntity); 
} 

// Attached object tracks modifications automatically 

context.SaveChanges(); 

Wenn Sie das Wissen über den Schlüssel des Objekts verwenden, können Sie so etwas wie diese verwenden können :

if (myEntity.Id != 0) 
{ 
    context.MyEntities.Attach(myEntity); 
    context.ObjectStateManager.ChangeObjectState(myEntity, EntityState.Modified); 
} 
else 
{ 
    context.MyEntities.AddObject(myEntity); 
} 

context.SaveChanges(); 

Wenn Sie Sie exectue Lookup-Abfrage muss nicht existance des Objekts durch seine Id entscheiden kann:

var id = myEntity.Id; 
if (context.MyEntities.Any(e => e.Id == id)) 
{ 
    context.MyEntities.Attach(myEntity); 
    context.ObjectStateManager.ChangeObjectState(myEntity, EntityState.Modified); 
} 
else 
{ 
    context.MyEntities.AddObject(myEntity); 
} 

context.SaveChanges(); 
  0

Danke. Sieht aus wie ich brauche. Darf ich Ihnen eine Frage stellen, die mich schon länger beschäftigt? Normalerweise lege ich meinen Kontext in einen kurzen "using" -Block. Ist es in Ordnung, den Kontext für eine Weile im Speicher zu belassen? Zum Beispiel während der Lebensdauer eines Windows-Formulars?Ich versuche normalerweise, Datenbankobjekte zu bereinigen, um eine minimale Belastung der Datenbank zu gewährleisten. Gibt es kein Problem, meinen EF Kontext zu zerstören? 06 apr. 112011-04-06 02:16:58

  0

Überprüfen Sie dies: http://StackOverflow.com/Questions/3653009/entity-framework-and-connection-pooling/3653392#3653392 Objekt Kontext sollte so kurz wie möglich leben, aber im Falle von winforms oder wpf kann dies bedeuten, dass Kontext ist so lange leben wie der Moderator. Die verknüpfte Frage enthält einen Link zum msdn-Artikel über die Verwendung von nhibernate session in winforms. Derselbe Ansatz kann für den Kontext verwendet werden. 06 apr. 112011-04-06 05:34:14

  0

Aber was, wenn ich dies mit einer Liste von Objekten tun muss ... in meiner Datenbank gibt es eine Liste von Zeilen mit der gleichen ID und ich möchte ersetzen, wenn thew existieren oder einfügen, wenn sie nicht .. wie mache ich es? Vielen Dank! 14 okt. 112011-10-14 11:45:52

+1

Diese Antwort LOOKS genial, aber ich bin auf dieses Problem bei der Aktualisierung: Ein Objekt mit dem gleichen Schlüssel existiert bereits im ObjectStateManager. Der ObjectStateManager kann mehrere Objekte mit demselben Schlüssel nicht verfolgen. 29 nov. 122012-11-29 21:58:14

+1

Sieht so aus, als hätte ich gerade ein kleines Problem mit dem Abrufen des vorhandenen Objekts, um den Schlüssel vor dem Update abzurufen. Das Lösen dieses Lookup-Objekts half zuerst, es zu beheben. 29 nov. 122012-11-29 22:15:28

  0

@JohnZ also hast du dann losgebunden, bin ich richtig? Ist ein solches Verhalten leistungsmäßig teuer? Ich habe das gleiche Problem. 15 jan. 142014-01-15 09:56:36

  0

@Anar Ja, ich habe einen Abruf gemacht, um die Datenbankversion des Objekts zu holen und einige seiner Werte herauszuziehen, dann habe ich diese Version entfernt, dann das Objekt an die Methode angehängt, seinen Objektstatus geändert und gespeichert. Und ja, es ist ziemlich teuer, weil Sie für jedes Element, das Sie aktualisieren, eine Auswahl aus der Datenbank treffen (aber nur, wie ich es mache). 15 jan. 142014-01-15 15:49:03

  0

@JohnZ Ich habe ein seltsames Problem, wo ich einen vorhandenen Datensatz abrufen, ändern Sie die Feldwerte, dann, wenn ich 'SaveChanges()' 'aufrufen wird irgendwie diese geänderten Werte an die Datenbank gesendet. Stattdessen sendet es das Objekt mit unveränderten Feldwerten. Ich habe das Gefühl, Sie kennen bereits eine Lösung für dieses Problem. Bitte schaut es euch an, ich würde es sehr schätzen. http://stackoverflow.com/questions/21088398/cannot-update-entity-framework-model 15 jan. 142014-01-15 15:56:00

  0

Würde es nicht fehlschlagen, wenn andere externe Prozesse den gleichen Datensatz ändern/hinzufügen können, direkt nachdem Sie dbcontext (im verbundenen Zustand) abgerufen haben .. 24 jun. 152015-06-24 07:39:53

  0

@LadislavMrnka danke Kumpel - wie würde das oben ändern, jetzt, dass wir Changetracker in ef6 haben? 14 dez. 162016-12-14 05:05:45

  0

Hi danke - noch eine Frage zum letzten Beispiel: Erkennt es Änderungen in Child-Objekten und aktualisiert diese Objekte? 20 dez. 162016-12-20 05:01:06

  0

@BKSpurgeon: Nein. Sie müssen auch Änderungen für untergeordnete Objekte erkennen, wenn Sie mit dem getrennten Entitätsbaum arbeiten. 24 dez. 162016-12-24 08:45:44


7

Wenn Sie wissen, dass Sie den gleichen Kontext und nicht die Ablösung keine Einheiten verwenden, können Sie eine generische Version wie folgt machen:

public void InsertOrUpdate<T>(T entity, DbContext db) where T : class 
{ 
    if (db.Entry(entity).State == EntityState.Detached) 
     db.Set<T>().Add(entity); 

    // If an immediate save is needed, can be slow though 
    // if iterating through many entities: 
    db.SaveChanges(); 
} 

db kann natürlich ein Klassenfeld sein, oder kann das Verfahren durchgeführt werden Statisch und eine Erweiterung, aber das ist die Basis.


25

Ab Entity Framework 4.3 gibt es eine AddOrUpdate Methode bei Namespace System.Data.Entity.Migrations:

public static void AddOrUpdate<TEntity>(
    this IDbSet<TEntity> set, 
    params TEntity[] entities 
) 
where TEntity : class 

, die von den doc:

Adds oder Updates Einheiten durch Schlüssel, wenn Savechanges aufgerufen wird. Äquivalent zu einer "upsert" -Operation aus der Datenbankterminologie. Diese Methode kann nützlich sein, wenn Sie Daten mit Migrationen eingrenzen.


die comment by @Smashing1978 zu beantworten, ich relevanten Teile von Link von @Colin bereitgestellt Paste wird

Die Aufgabe AddOrUpdate ist, um sicherzustellen, dass Sie schaffen keine Duplikate , wenn Sie Daten Saatgut während der Entwicklung.

Erstens wird es eine Abfrage in der Datenbank ausführen für einen Datensatz , wo alles, was Sie als Schlüssel (erster Parameter) geliefert entspricht den abgebildet Spalt Wert (oder Werte) in der AddOrUpdate geliefert. So das ist ein wenig loosey-goosey für das Zusammenbringen aber vollkommen in Ordnung für das Seeding Entwurfszeitdaten.

Noch wichtiger, wenn eine Übereinstimmung gefunden wird, aktualisiert das Update alle und null, die nicht in Ihrem AddOrUpdate waren.

Das heißt, ich habe eine Situation, wo ich Daten von einem externen Dienst am Ziehen und Einfügen oder vorhandene Werte durch Primärschlüssel zu aktualisieren (und meine lokalen Daten für die Verbraucher ist schreibgeschützt) - wurde AddOrUpdate in der Produktion für mehr als 6 Monate jetzt und bisher keine Probleme.

+6

Der Namespace System.Data.Entity.Migrations enthält Klassen für codebasierte Migrationen und ihre Konfigurationen. Gibt es einen Grund, warum wir dies nicht in unseren Repositories für die Nichtmigrationseinheit AddOrUpdates verwenden sollten? 24 feb. 152015-02-24 20:58:55

+5

Mach's gut mit der AddOrUpdate Methode: http://thedatafarm.com/data-access/take-care-with-ef-4-3-addorupdate-method/ 14 mai. 152015-05-14 08:43:46


3

Ladislavs Antwort war knapp, aber ich musste einige Änderungen vornehmen, um dies in EF6 (Datenbank-zuerst) zum Laufen zu bringen.Ich streckte meine Daten Zusammenhang mit meiner auf AddOrUpdate Methode und bisher scheint dies mit abgesetzter Objekten gut arbeiten werden:

using System.Data.Entity; 

[....] 

public partial class MyDBEntities { 

    public void AddOrUpdate(MyDBEntities ctx, DbSet set, Object obj, long ID) { 
     if (ID != 0) { 
      set.Attach(obj); 
      ctx.Entry(obj).State = EntityState.Modified; 
     } 
     else { 
      set.Add(obj); 
     } 
    } 
[....] 

3

Die Magie passiert, wenn SaveChanges() und hängt von den aktuellen EntityState aufrufen. Wenn die Entität eine EntityState.Added hat, wird sie zur Datenbank hinzugefügt, wenn sie eine EntityState.Modified hat, wird sie in der Datenbank aktualisiert. So können Sie eine InsertOrUpdate() Methode implementieren wie folgt:

public void InsertOrUpdate(Blog blog) 
{ 
    using (var context = new BloggingContext()) 
    { 
     context.Entry(blog).State = blog.BlogId == 0 ? 
            EntityState.Added : 
            EntityState.Modified; 

     context.SaveChanges(); 
    } 
} 

More about EntityState

Wenn Sie nicht auf Id = 0 zu überprüfen, um festzustellen, ob es sich um eine neue Einheit ist oder nicht, überprüfen Sie die answer of Ladislav Mrnka.


1

Meiner Meinung nach ist es wert zu sagen, dass Sie mit der neu veröffentlichten EntityGraphOperations for Entity Framework Code First Sie vor dem Schreiben einige repetitive Codes für die Definition der Zustände aller Entitäten in der Grafik speichern können. Ich bin der Autor dieses Produktes. Und ich habe es in der github, code-project veröffentlicht (enthält eine schrittweise Demonstration und ein Beispielprojekt steht zum Download bereit) und nuget.

Es wird automatisch den Zustand der Einheiten zu Added oder Modified gesetzt. Und Sie wählen manuell aus, welche Entitäten gelöscht werden müssen, wenn sie nicht mehr existieren.

Das Beispiel:

Lasst uns sagen, ich habe ein Person Objekt. Person könnte viele Telefone, ein Dokument und könnte einen Ehepartner haben.

public class Person 
{ 
    public int Id { get; set; } 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
    public string MiddleName { get; set; } 
    public int Age { get; set; } 
    public int DocumentId {get; set;} 

    public virtual ICollection<Phone> Phones { get; set; } 
    public virtual Document Document { get; set; } 
    public virtual PersonSpouse PersonSpouse { get; set; } 
} 

Ich möchte den Zustand aller Entitäten bestimmen, die in der Grafik enthalten ist.

context.InsertOrUpdateGraph(person) 
     .After(entity => 
     { 
      // Delete missing phones. 
      entity.HasCollection(p => p.Phones) 
       .DeleteMissingEntities(); 

      // Delete if spouse is not exist anymore. 
      entity.HasNavigationalProperty(m => m.PersonSpouse) 
        .DeleteIfNull(); 
     }); 

Auch wie Sie wissen, können eindeutige Schlüsseleigenschaften beim Definieren des Status von Phone entity eine Rolle spielen. Für solche speziellen Zwecke haben wir ExtendedEntityTypeConfiguration<> Klasse, die von EntityTypeConfiguration<> erbt. Wenn wir solche speziellen Konfigurationen verwenden wollen, müssen wir unsere Mapping-Klassen von ExtendedEntityTypeConfiguration<> anstelle von EntityTypeConfiguration<> erben. Zum Beispiel:

public class PhoneMap: ExtendedEntityTypeConfiguration<Phone> 
    { 
     public PhoneMap() 
     { 
      // Primary Key 
      this.HasKey(m => m.Id); 
       … 
      // Unique keys 
      this.HasUniqueKey(m => new { m.Prefix, m.Digits }); 
     } 
    } 

Das ist alles.


2

einfügen sonst Update beid

public void InsertUpdateData() 
{ 
//Here TestEntities is the class which is given from "Save entity connection setting in web.config" 
TestEntities context = new TestEntities(); 

var query = from data in context.Employee 
      orderby data.name 
      select data; 

foreach (Employee details in query) 
{ 
    if (details.id == 1) 
    { 
     //Assign the new values to name whose id is 1 
     details.name = "Sanjay"; 
     details. Surname="Desai"; 
     details.address=" Desiwadi"; 
    } 
    else if(query==null) 
    { 
    details.name="Sharad"; 
    details.surname=" Chougale "; 
    details.address=" Gargoti"; 
} 

//Save the changes back to database. 
context.SaveChanges(); 
} 
  0

Ich habe diesen Ansatz aber und überprüft (nach dem ersten oder Standard) if (Abfrage == null) 27 jan. 182018-01-27 16:40:00