Hat jemand Vorschläge zur effizientesten Implementierung der Logik "Zeile aktualisieren, falls vorhanden, sonst einfügen" mithilfe von Entity Framework?
Hat jemand Vorschläge zur effizientesten Implementierung der Logik "Zeile aktualisieren, falls vorhanden, sonst einfügen" mithilfe von Entity Framework?
Antworten:
Wenn Sie mit angehängten Objekten arbeiten (Objekte, die aus derselben Instanz des Kontexts geladen wurden), können Sie einfach Folgendes verwenden:
if (context.ObjectStateManager.GetObjectStateEntry(myEntity).State == EntityState.Detached)
{
context.MyEntities.AddObject(myEntity);
}
// Attached object tracks modifications automatically
context.SaveChanges();
Wenn Sie Kenntnisse über den Schlüssel des Objekts verwenden können, können Sie Folgendes verwenden:
if (myEntity.Id != 0)
{
context.MyEntities.Attach(myEntity);
context.ObjectStateManager.ChangeObjectState(myEntity, EntityState.Modified);
}
else
{
context.MyEntities.AddObject(myEntity);
}
context.SaveChanges();
Wenn Sie die Existenz des Objekts nicht anhand seiner ID bestimmen können, müssen Sie eine Suchabfrage ausführen:
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();
using
Block. Ist es in Ordnung, den Kontext für eine Weile im Gedächtnis zu lassen? Zum Beispiel während der Lebensdauer eines Windows-Formulars? Normalerweise versuche ich, Datenbankobjekte zu bereinigen, um eine minimale Belastung der Datenbank sicherzustellen. Gibt es kein Problem damit, meinen EF-Kontext zu zerstören?
Ab Entity Framework 4.3 gibt es AddOrUpdate
im Namespace eine Methode System.Data.Entity.Migrations
:
public static void AddOrUpdate<TEntity>(
this IDbSet<TEntity> set,
params TEntity[] entities
)
where TEntity : class
welche von der doc :
Fügt Entitäten nach Schlüssel hinzu oder aktualisiert sie, wenn SaveChanges aufgerufen wird. Entspricht einer "Upsert" -Operation aus der Datenbankterminologie. Diese Methode kann beim Seeding von Daten mithilfe von Migrationen hilfreich sein.
Um den Kommentar von @ Smashing1978 zu beantworten , füge ich relevante Teile aus dem von @Colin bereitgestellten Link ein
Die Aufgabe von AddOrUpdate besteht darin, sicherzustellen, dass Sie keine Duplikate erstellen, wenn Sie Daten während der Entwicklung festlegen.
Zunächst wird eine Abfrage in Ihrer Datenbank ausgeführt, in der nach einem Datensatz gesucht wird, bei dem alles, was Sie als Schlüssel (erster Parameter) angegeben haben, mit dem im AddOrUpdate angegebenen zugeordneten Spaltenwert (oder den zugeordneten Werten) übereinstimmt. Dies ist also ein wenig locker für das Matching, aber perfekt für das Seeding von Entwurfszeitdaten.
Noch wichtiger ist, wenn eine Übereinstimmung gefunden wird, aktualisiert das Update alle und nullt alle, die nicht in Ihrem AddOrUpdate enthalten waren.
Ich habe jedoch eine Situation, in der ich Daten von einem externen Dienst abrufe und vorhandene Werte per Primärschlüssel einfüge oder aktualisiere (und meine lokalen Daten für Verbraucher schreibgeschützt sind) - seit AddOrUpdate
mehr als 6 Monaten in der Produktion bisher keine probleme.
Die Magie passiert beim Anrufen SaveChanges()
und hängt vom Strom ab EntityState
. Wenn die Entität eine hat EntityState.Added
, wird sie der Datenbank hinzugefügt. Wenn sie eine hat EntityState.Modified
, wird sie in der Datenbank aktualisiert. Sie können also eine InsertOrUpdate()
Methode wie folgt implementieren :
public void InsertOrUpdate(Blog blog)
{
using (var context = new BloggingContext())
{
context.Entry(blog).State = blog.BlogId == 0 ?
EntityState.Added :
EntityState.Modified;
context.SaveChanges();
}
}
Wenn Sie nicht überprüfen können Id = 0
, ob es sich um eine neue Einheit handelt oder nicht, überprüfen Sie die Antwort von Ladislav Mrnka .
Wenn Sie wissen, dass Sie denselben Kontext verwenden und keine Entitäten trennen, können Sie eine generische Version wie die folgende erstellen:
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 die Methode kann statisch und eine Erweiterung gemacht werden, aber das sind die Grundlagen.
Ladislavs Antwort war knapp, aber ich musste einige Änderungen vornehmen, damit dies in EF6 (Datenbank zuerst) funktioniert. Ich habe meinen Datenkontext mit meiner Methode on AddOrUpdate erweitert, und bisher scheint dies mit getrennten Objekten gut zu funktionieren:
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);
}
}
[....]
Meiner Meinung nach ist es erwähnenswert, dass Sie sich mit dem neu veröffentlichten EntityGraphOperations for Entity Framework-Code zunächst das Schreiben einiger sich wiederholender Codes zum Definieren der Zustände aller Entitäten im Diagramm ersparen können. Ich bin der Autor dieses Produkts. Und ich habe es im Github , im Code-Projekt ( einschließlich einer schrittweisen Demonstration und zum Herunterladen eines Beispielprojekts) und im Nuget veröffentlicht .
Der Status der Entitäten wird automatisch auf Added
oder gesetzt Modified
. Und Sie werden manuell auswählen, welche Entitäten gelöscht werden müssen, wenn sie nicht mehr vorhanden sind.
Das Beispiel:
Nehmen wir an, ich habe ein Person
Objekt. Person
könnte viele Telefone haben, 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 Status aller Entitäten bestimmen, die in der Grafik enthalten sind.
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();
});
Wie Sie wissen, können beim Definieren des Status der Telefonentität eindeutige Schlüsseleigenschaften eine Rolle spielen. Für solche speziellen Zwecke haben wir eine ExtendedEntityTypeConfiguration<>
Klasse, die von erbt EntityTypeConfiguration<>
. Wenn wir solche speziellen Konfigurationen verwenden möchten, müssen wir unsere Zuordnungsklassen von erben ExtendedEntityTypeConfiguration<>
und nicht von EntityTypeConfiguration<>
. Beispielsweise:
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.
Fügen Sie else ein, aktualisieren Sie beide
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();
}
Überprüfen Sie die vorhandene Zeile mit Beliebig.
public static void insertOrUpdateCustomer(Customer customer)
{
using (var db = getDb())
{
db.Entry(customer).State = !db.Customer.Any(f => f.CustomerId == customer.CustomerId) ? EntityState.Added : EntityState.Modified;
db.SaveChanges();
}
}
Alternative für @LadislavMrnka Antwort. Dies gilt für Entity Framework 6.2.0.
Wenn Sie ein bestimmtes DbSet
Element haben, das entweder aktualisiert oder erstellt werden muss:
var name = getNameFromService();
var current = _dbContext.Names.Find(name.BusinessSystemId, name.NameNo);
if (current == null)
{
_dbContext.Names.Add(name);
}
else
{
_dbContext.Entry(current).CurrentValues.SetValues(name);
}
_dbContext.SaveChanges();
Dies kann jedoch auch für ein Generikum DbSet
mit einem einzelnen Primärschlüssel oder einem zusammengesetzten Primärschlüssel verwendet werden.
var allNames = NameApiService.GetAllNames();
GenericAddOrUpdate(allNames, "BusinessSystemId", "NameNo");
public virtual void GenericAddOrUpdate<T>(IEnumerable<T> values, params string[] keyValues) where T : class
{
foreach (var value in values)
{
try
{
var keyList = new List<object>();
//Get key values from T entity based on keyValues property
foreach (var keyValue in keyValues)
{
var propertyInfo = value.GetType().GetProperty(keyValue);
var propertyValue = propertyInfo.GetValue(value);
keyList.Add(propertyValue);
}
GenericAddOrUpdateDbSet(keyList, value);
//Only use this when debugging to catch save exceptions
//_dbContext.SaveChanges();
}
catch
{
throw;
}
}
_dbContext.SaveChanges();
}
public virtual void GenericAddOrUpdateDbSet<T>(List<object> keyList, T value) where T : class
{
//Get a DbSet of T type
var someDbSet = Set(typeof(T));
//Check if any value exists with the key values
var current = someDbSet.Find(keyList.ToArray());
if (current == null)
{
someDbSet.Add(value);
}
else
{
Entry(current).CurrentValues.SetValues(value);
}
}
Korrigiert
public static void InsertOrUpdateRange<T, T2>(this T entity, List<T2> updateEntity)
where T : class
where T2 : class
{
foreach(var e in updateEntity)
{
context.Set<T2>().InsertOrUpdate(e);
}
}
public static void InsertOrUpdate<T, T2>(this T entity, T2 updateEntity)
where T : class
where T2 : class
{
if (context.Entry(updateEntity).State == EntityState.Detached)
{
if (context.Set<T2>().Any(t => t == updateEntity))
{
context.Set<T2>().Update(updateEntity);
}
else
{
context.Set<T2>().Add(updateEntity);
}
}
context.SaveChanges();
}