Хтось має пропозиції щодо найефективнішого способу реалізації логіки "оновлення рядка, якщо вона ще є", використовуючи Entity Framework?
Хтось має пропозиції щодо найефективнішого способу реалізації логіки "оновлення рядка, якщо вона ще є", використовуючи Entity Framework?
Відповіді:
Якщо ви працюєте з доданим об'єктом (об'єктом, завантаженим з одного і того ж екземпляру контексту), ви можете просто використовувати:
if (context.ObjectStateManager.GetObjectStateEntry(myEntity).State == EntityState.Detached)
{
context.MyEntities.AddObject(myEntity);
}
// Attached object tracks modifications automatically
context.SaveChanges();
Якщо ви можете використовувати будь-які знання про ключ об'єкта, ви можете використовувати щось подібне:
if (myEntity.Id != 0)
{
context.MyEntities.Attach(myEntity);
context.ObjectStateManager.ChangeObjectState(myEntity, EntityState.Modified);
}
else
{
context.MyEntities.AddObject(myEntity);
}
context.SaveChanges();
Якщо ви не можете вирішити існування об'єкта за його Id, ви повинні виконати запит пошуку:
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
блоці. Чи добре залишити контекст на пам'ять на деякий час? Наприклад, протягом життя форми Windows? Я зазвичай намагаюся очистити об'єкти бази даних, щоб забезпечити мінімальне навантаження на базу даних. Чи не чекає жодної проблеми, щоб знищити мій контекст EF?
Станом на Entity Framework 4.3, AddOrUpdate
у просторі імен існує метод System.Data.Entity.Migrations
:
public static void AddOrUpdate<TEntity>(
this IDbSet<TEntity> set,
params TEntity[] entities
)
where TEntity : class
який док :
Додає або оновлює об'єкти за ключем, коли викликається SaveChanges. Еквівалентно операції "вдосконалення" з термінології бази даних. Цей метод може бути корисним при висіванні даних за допомогою міграції.
Щоб відповісти на коментар @ Smashing1978 , я вставлю відповідні частини із посилання, наданого @Colin
Завдання AddOrUpdate полягає в тому, щоб не створювати дублікатів, коли ви збираєте дані під час розробки.
По-перше, він виконає запит у вашій базі даних, шукаючи запис, де все, що ви надали як ключ (перший параметр), відповідає зіставленим значенням стовпця (або значенням), що подається в AddOrUpdate. Таким чином, це трохи нещільно гусячий для відповідності, але ідеально підходить для висівання даних про час дизайну.
Що ще важливіше, якщо відповідність буде знайдена, то оновлення оновить усе та видалить усе, що не було у вашому AddOrUpdate.
З цього приводу я маю ситуацію, коли я витягую дані із зовнішньої служби та вставляю або оновлюю існуючі значення первинним ключем (а мої локальні дані для споживачів є лише для читання) - використовую AddOrUpdate
у виробництві вже більше 6 місяців далеко не проблем.
Магія відбувається при дзвінку SaveChanges()
і залежить від струму EntityState
. Якщо сутність має EntityState.Added
, вона буде додана до бази даних, якщо вона має EntityState.Modified
, вона буде оновлена в базі даних. Отже, ви можете реалізувати InsertOrUpdate()
метод таким чином:
public void InsertOrUpdate(Blog blog)
{
using (var context = new BloggingContext())
{
context.Entry(blog).State = blog.BlogId == 0 ?
EntityState.Added :
EntityState.Modified;
context.SaveChanges();
}
}
Якщо ви не можете зареєструватися, Id = 0
щоб визначити, чи це нова організація чи ні, перевірте відповідь Ладіслава Мрнка .
Якщо ви знаєте, що ви використовуєте той самий контекст і не від'єднуєте жодних сутностей, ви можете зробити загальну версію, як ця:
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
Звичайно, це може бути поле класу, або метод може бути статичним і розширенням, але це основи.
Відповідь Ладіслава була близькою, але мені довелося внести кілька модифікацій, щоб змусити це працювати в EF6 (перша база даних). Я розширив свій контекст даних моїм методом AddOrUpdate, і поки що, здається, це добре працює з відокремленими об'єктами:
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);
}
}
[....]
На мою думку, варто сказати, що з нещодавно випущеним EntityGraphOperations for Entity Framework Code Спочатку ви можете вберегти себе від написання деяких повторюваних кодів для визначення станів усіх сутностей у графі. Я автор цього продукту. І я опублікував це в github , коді-проект ( включає поетапну демонстрацію і зразок проекту готовий до завантаження) та nuget .
Він автоматично встановить стан суб'єктів на Added
або Modified
. І ви вручну виберете, які об’єкти потрібно видалити, якщо його більше не існує.
Приклад:
Скажімо, я отримав Person
об’єкт. Person
могла мати багато телефонів, Документ і могла мати подружжя.
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; }
}
Я хочу визначити стан усіх об'єктів, який включений у графік.
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();
});
Також, як відомо, унікальні ключові властивості можуть грати роль під час визначення стану сутності телефону. Для таких спеціальних цілей у нас є ExtendedEntityTypeConfiguration<>
клас, який успадковує від EntityTypeConfiguration<>
. Якщо ми хочемо використовувати такі спеціальні конфігурації, то ми повинні успадковувати наші класи відображення ExtendedEntityTypeConfiguration<>
, а не EntityTypeConfiguration<>
. Наприклад:
public class PhoneMap: ExtendedEntityTypeConfiguration<Phone>
{
public PhoneMap()
{
// Primary Key
this.HasKey(m => m.Id);
…
// Unique keys
this.HasUniqueKey(m => new { m.Prefix, m.Digits });
}
}
Це все.
Вставте інше оновлення обох
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();
}
Перевірте наявний рядок за допомогою Any.
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();
}
}
Альтернатива для відповіді на @LadislavMrnka. Це якщо для Entity Framework 6.2.0.
Якщо у вас є конкретний DbSet
та елемент, який потрібно оновити або створити:
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();
Однак це також може бути використане для загального DbSet
з одним первинним ключем або складеним первинним ключем.
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);
}
}
Виправлено
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();
}