Qualcuno ha suggerimenti sul modo più efficiente per implementare la logica "aggiorna riga se esiste altrimenti inserisci" utilizzando Entity Framework?
Qualcuno ha suggerimenti sul modo più efficiente per implementare la logica "aggiorna riga se esiste altrimenti inserisci" utilizzando Entity Framework?
Risposte:
Se stai lavorando con un oggetto collegato (oggetto caricato dalla stessa istanza del contesto) puoi semplicemente usare:
if (context.ObjectStateManager.GetObjectStateEntry(myEntity).State == EntityState.Detached)
{
context.MyEntities.AddObject(myEntity);
}
// Attached object tracks modifications automatically
context.SaveChanges();
Se puoi usare qualsiasi conoscenza della chiave dell'oggetto, puoi usare qualcosa del genere:
if (myEntity.Id != 0)
{
context.MyEntities.Attach(myEntity);
context.ObjectStateManager.ChangeObjectState(myEntity, EntityState.Modified);
}
else
{
context.MyEntities.AddObject(myEntity);
}
context.SaveChanges();
Se non riesci a decidere l'esistenza dell'oggetto in base al suo ID, devi eseguire la query di ricerca:
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
blocco. Va bene lasciare il contesto in memoria per un po '? Ad esempio, durante la vita di un modulo di Windows? Di solito provo a ripulire gli oggetti del database per garantire il carico minimo sul database. Non ci sono problemi in attesa di distruggere il mio contesto EF?
A partire da Entity Framework 4.3, esiste un AddOrUpdate
metodo nello spazio dei nomi System.Data.Entity.Migrations
:
public static void AddOrUpdate<TEntity>(
this IDbSet<TEntity> set,
params TEntity[] entities
)
where TEntity : class
che dal doc :
Aggiunge o aggiorna le entità per chiave quando viene chiamato SaveChanges. Equivale a un'operazione "upsert" dalla terminologia del database. Questo metodo può essere utile durante il seeding dei dati utilizzando Migrations.
Per rispondere al commento di @ Smashing1978 , incollerò le parti pertinenti dal link fornito da @Colin
Il compito di AddOrUpdate è quello di assicurarsi di non creare duplicati quando si seminano dati durante lo sviluppo.
Innanzitutto, eseguirà una query nel database alla ricerca di un record in cui tutto ciò che è stato fornito come chiave (primo parametro) corrisponde al valore (o ai valori) della colonna mappata fornito in AddOrUpdate. Quindi questo è un po 'ficcanaso per l'abbinamento ma perfettamente perfetto per seminare i dati del tempo di progettazione.
Ancora più importante, se viene trovata una corrispondenza, l'aggiornamento aggiornerà tutto e annullerà quelli non presenti in AddOrUpdate.
Detto questo, ho una situazione in cui sto estraendo i dati da un servizio esterno e inserendo o aggiornando i valori esistenti tramite chiave primaria (ei miei dati locali per i consumatori sono di sola lettura) - sono AddOrUpdate
in uso da più di 6 mesi ormai e così di gran lunga nessun problema.
La magia accade quando si chiama SaveChanges()
e dipende dalla corrente EntityState
. Se l'entità ha un EntityState.Added
, verrà aggiunta al database, se ha un EntityState.Modified
, verrà aggiornata nel database. Quindi puoi implementare un InsertOrUpdate()
metodo come segue:
public void InsertOrUpdate(Blog blog)
{
using (var context = new BloggingContext())
{
context.Entry(blog).State = blog.BlogId == 0 ?
EntityState.Added :
EntityState.Modified;
context.SaveChanges();
}
}
Ulteriori informazioni su EntityState
Se non riesci a verificare Id = 0
se si tratta di una nuova entità o meno, controlla la risposta di Ladislav Mrnka .
Se sai che stai utilizzando lo stesso contesto e non stai staccando alcuna entità, puoi creare una versione generica come questa:
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
può ovviamente essere un campo di classe o il metodo può essere reso statico e un'estensione, ma questa è la base.
La risposta di Ladislav è stata vicina ma ho dovuto apportare un paio di modifiche per farlo funzionare in EF6 (prima il database). Ho esteso il mio contesto di dati con il mio metodo on AddOrUpdate e finora questo sembra funzionare bene con oggetti distaccati:
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);
}
}
[....]
A mio avviso, vale la pena di dire che con il nuovo EntityGraphOperations per Entity Framework Code in primo luogo è possibile salvarsi dalla scrittura di alcuni codici ripetitivi per la definizione degli stati di tutte le entità nel grafico. Sono l'autore di questo prodotto. E l'ho pubblicato su github , code-project ( include una dimostrazione passo-passo e un progetto di esempio è pronto per il download) e nuget .
Sarà impostare automaticamente lo stato delle entità a Added
o Modified
. E sceglierai manualmente quali entità devono essere eliminate se non esistono più.
L'esempio:
Diciamo che ho un Person
oggetto. Person
potrebbe avere molti telefoni, un documento e potrebbe avere un coniuge.
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; }
}
Voglio determinare lo stato di tutte le entità che è incluso nel grafico.
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();
});
Inoltre, come sapete, proprietà chiave uniche potrebbero svolgere un ruolo durante la definizione dello stato dell'entità Telefono. Per tali scopi speciali abbiamo ExtendedEntityTypeConfiguration<>
classe, che eredita da EntityTypeConfiguration<>
. Se vogliamo usare configurazioni così speciali, dobbiamo ereditare le nostre classi di mappatura ExtendedEntityTypeConfiguration<>
piuttosto che EntityTypeConfiguration<>
. Per esempio:
public class PhoneMap: ExtendedEntityTypeConfiguration<Phone>
{
public PhoneMap()
{
// Primary Key
this.HasKey(m => m.Id);
…
// Unique keys
this.HasUniqueKey(m => new { m.Prefix, m.Digits });
}
}
È tutto.
Inserisci altro aggiorna entrambi
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();
}
Controlla la riga esistente con Qualsiasi.
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();
}
}
Alternativa alla risposta di @LadislavMrnka. Questo se per Entity Framework 6.2.0.
Se si dispone di uno specifico DbSet
e un elemento che deve essere aggiornato o creato:
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();
Tuttavia, questo può essere utilizzato anche per un generico DbSet
con una chiave primaria singola o una chiave primaria composita.
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);
}
}
corretto
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();
}