Hat jemand Vorschläge für die effizienteste Art der Implementierung der "Update-Zeile, wenn es sonst Einfügung gibt" mit Entity Framework?
Wenn Sie mit einem angehängten Objekt arbeiten (Objekt, das von derselben Instanz des Kontextes geladen wird), 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 Wissen ü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 nicht entscheiden können, ob das Objekt anhand seiner ID vorhanden ist, müssen Sie die Abfrage abfragen:
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();
Ab Entity Framework 4.3 gibt es im Namespace System.Data.Entity.Migrations
eine AddOrUpdate
-Methode:
public static void AddOrUpdate<TEntity>(
this IDbSet<TEntity> set,
params TEntity[] entities
)
where TEntity : class
welche durch die doc :
Fügt Entitäten nach Schlüssel hinzu, wenn SaveChanges aufgerufen wird. Äquivalent zu einer "Upert" -Operation von der Datenbank-Terminologie. Diese Methode kann .__ sein. nützlich beim Seeding von Daten mit Migrationen.
Um den Kommentar von @ Smashing1978 zu beantworten, werde ich relevante Teile aus dem von @Colin bereitgestellten Link einfügen
Die Aufgabe von AddOrUpdate ist es sicherzustellen, dass Sie keine Duplikate erstellen wenn Sie Daten während der Entwicklung säen.
Zuerst führt es eine Abfrage in Ihrer Datenbank aus und sucht nach einem Datensatz Was auch immer Sie als Schlüssel (erster Parameter) angegeben haben, stimmt mit dem .__ überein. Zugeordneter Spaltenwert (oder Werte), die im AddOrUpdate bereitgestellt werden. Also das ist ein wenig locker-goosey für das Zusammenbringen aber vollkommen für das Säen Entwurfszeitdaten.
Noch wichtiger ist, wenn eine Übereinstimmung gefunden wird, aktualisiert das Update alle und löschen Sie alle, die sich nicht in Ihrem AddOrUpdate befanden.
Das heißt, ich habe eine Situation, in der ich Daten von einem externen Dienst abrufe und vorhandene Werte per Primärschlüssel einfügte oder aktualisiere (und meine lokalen Daten für Verbraucher schreibgeschützt sind). Ich habe AddOrUpdate
bereits seit mehr als 6 Monaten in der Produktion verwendet bisher keine probleme.
Wenn Sie wissen, dass Sie denselben Kontext verwenden und keine Entitäten trennen, können Sie eine generische Version wie folgt 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 als Erweiterung erstellt werden. Dies ist jedoch die Grundlagen.
Die Magie geschieht beim Aufruf von SaveChanges()
und hängt von der aktuellen EntityState
ab. Wenn die Entität über einen EntityState.Added
verfügt, wird sie der Datenbank hinzugefügt. Wenn sie über einen EntityState.Modified
verfügt, wird sie in der Datenbank aktualisiert. So können Sie 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 auf Id = 0
prüfen können, ob es sich um eine neue Entität handelt oder nicht, überprüfen Sie das Antwort von Ladislav Mrnka .
Ladislavs Antwort war knapp, aber ich musste einige Änderungen vornehmen, damit dies in EF6 (Datenbank zuerst) funktioniert. Ich habe meinen Datenkontext mit meiner on AddOrUpdate-Methode erweitert, und bis jetzt scheint das mit freistehenden 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);
}
}
[....]
Fügen Sie ein anderes Update ein
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();
}
Meiner Meinung nach ist es wichtig zu erwähnen, dass Sie sich mit dem neu veröffentlichten EntityGraphOperations for Entity Framework Code First ersparen können, einige sich wiederholende Codes zum Definieren der Zustände aller Entitäten im Diagramm zu schreiben. Ich bin der Autor dieses Produkts. Und ich habe es im github , code-project veröffentlicht ( enthält eine schrittweise Demonstration und ein Beispielprojekt steht zum Herunterladen bereit) und Nuget .
Es setzt automatisch den Status der Entitäten auf Added
oder Modified
. Und Sie müssen manuell auswählen, welche Entitäten gelöscht werden sollen, wenn sie nicht mehr existieren.
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 eindeutige Schlüsseleigenschaften beim Definieren des Status der Telefonentität eine Rolle spielen. Für solche speziellen Zwecke haben wir die Klasse ExtendedEntityTypeConfiguration<>
, Die von EntityTypeConfiguration<>
Erbt. Wenn wir solche speziellen Konfigurationen verwenden möchten, müssen wir unsere Zuordnungsklassen von ExtendedEntityTypeConfiguration<>
Und nicht 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.
Alternative für die Antwort von @LadislavMrnka. Dies gilt für Entity Framework 6.2.0.
Wenn Sie ein bestimmtes DbSet
und ein 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 generische 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);
}
}
Ü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();
}
}