Обновить строку, если она существует в Else Insert Logic с платформой Entity


125

Есть ли у кого-нибудь предложения по наиболее эффективному способу реализовать логику «обновить строку, если она существует в другом месте», используя Entity Framework?

+2

Это то, что должно быть сделано на уровне ядра базы данных, в хранимой процедуре. В противном случае вам придется обернуть обнаружение/обновление/вставку в транзакции. 06 апр. 112011-04-06 09:12:11

+1

@ Stephen: На самом деле это то, что я закончил делать. Благодарю. 06 апр. 112011-04-06 14:18:43

  0

Джонатан, ваш вопрос очень полезен для меня. Почему вы переключились на хранимую процедуру? 15 янв. 142014-01-15 09:54:35

+2

@Anar: Это было просто легче, и я ожидаю гораздо более эффективного. 15 янв. 142014-01-15 16:58:03

  0

Вам нужно написать хранимую процедуру для каждой таблицы? 03 янв. 182018-01-03 05:00:58

130

Если вы работаете с прикрепленным объектом (объект загружен из одного экземпляра контекста) вы можете просто использовать:

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 вы должны exectue подстановок запрос:

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

Спасибо. Похоже, что мне нужно. Могу я задать вам один вопрос, который меня беспокоил какое-то время? Обычно я помещаю свой контекст в короткий блок 'using'. Можно ли оставить контекст в памяти какое-то время? Например, в течение жизни формы Windows?Обычно я пытаюсь очистить объекты базы данных, чтобы обеспечить минимальную нагрузку на базу данных. Нет ли проблем, ожидающих разрушения моего EF-контекста? 06 апр. 112011-04-06 02:16:58

  0

Проверьте это: http://stackoverflow.com/questions/3653009/entity-framework-and-connection-pooling/3653392#3653392 контекст объекта должен быть как можно короче, но в случае winforms или wpf это может означать, что контекст до тех пор, пока ведущий. Связанный вопрос содержит ссылку на статью msdn об использовании сеанса nhibernate в winforms. Тот же подход может использоваться для контекста. 06 апр. 112011-04-06 05:34:14

  0

Но что, если мне нужно сделать это со списком объектов ... в моей базе данных есть список строк с одним и тем же идентификатором, и я хочу заменить, если они существуют или вставляются, если они не ... как я это делаю? благодаря! 14 окт. 112011-10-14 11:45:52

+1

Этот ответ СМОТРЕТСЯ потрясающе, но я столкнулся с этой проблемой при обновлении: объект с тем же ключом уже существует в ObjectStateManager. ObjectStateManager не может отслеживать несколько объектов с одним и тем же ключом. 29 ноя. 122012-11-29 21:58:14

+1

Похоже, у меня возникла небольшая проблема с извлечением существующего объекта, чтобы получить его ключ перед выполнением обновления; отключение этого объекта поиска помогло его исправить. 29 ноя. 122012-11-29 22:15:28

  0

@JohnZ, поэтому вы отделились, затем приложили, я прав? Является ли такое поведение дорогостоящим? У меня такая же проблема. 15 янв. 142014-01-15 09:56:36

  0

@Anar да, я сделал выборку, чтобы получить версию базы данных объекта и вытащить некоторые из ее значений, затем я отделил эту версию, затем привязал объект, переданный в метод, изменил его состояние объекта и сохранил. И да, это довольно дорого, потому что вы делаете выбор из базы данных для каждого элемента, который вы обновляете (но только в том, как я это делаю). 15 янв. 142014-01-15 15:49:03

  0

@JohnZ У меня возникает странная проблема, когда я беру существующую запись, меняю ее значения поля, а затем, когда я вызываю 'SaveChanges()', как-то не отправляет эти измененные значения в базу данных. Вместо этого он отправляет объект с неизменными значениями поля. У меня такое чувство, что вы уже знаете решение этой проблемы. Пожалуйста, взгляните, я бы очень признателен. http://stackoverflow.com/questions/21088398/cannot-update-entity-framework-model 15 янв. 142014-01-15 15:56:00

  0

Не провалится ли это, если другие внешние процессы могут изменить/добавить одну и ту же запись сразу после того, как вы восстановили dbcontext (в состоянии подключения). 24 июн. 152015-06-24 07:39:53

  0

@LadislavMrnka благодарит мату - как изменилось бы это сейчас, когда у нас есть changetracker в ef6? 14 дек. 162016-12-14 05:05:45

  0

привет спасибо - еще один вопрос для последнего приведенного примера: узнает ли он изменения в дочерних объектах и ​​обновит эти объекты? 20 дек. 162016-12-20 05:01:06

  0

@BKSpurgeon: Нет. Вы должны также определять изменения для дочерних объектов при работе с деревом отдельных объектов. 24 дек. 162016-12-24 08:45:44


7

Если вы знаете, что вы используете один и тот же контекст, а не отделяя любые объекты, вы можете сделать общую версию так:

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, конечно, может быть поле класса или метод может быть статический и расширение, но это основы.


25

По Entity Framework 4.3, существует AddOrUpdate метод в пространстве имен System.Data.Entity.Migrations:

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

, который по doc:

Добавляет или обновляет лиц по ключу, когда SaveChanges называется. Эквивалент на операцию «upsert» из базы данных. Этот метод может быть полезен при посеве данных с использованием миграции.


Чтобы ответить на comment by @Smashing1978, я буду вставлять соответствующие части из ссылки, предоставленные @Colin

Работа AddOrUpdate это гарантирует, что вы не создавать дубликаты , когда семена данных во время разработки.

Во-первых, он будет выполнять запрос в базе данных ищет записи , где все, что вы предоставили в качестве основного (первого параметра) соответствует отображенное значение столбца (или значения), поступающее в AddOrUpdate. Таким образом, этот является немного рыхлым-гусиным для соответствия, но отлично подходит для посева. данные о времени разработки.

Что еще более важно, если совпадение найдено, обновление обновит все и удалит все, что не было в вашем AddOrUpdate.

Тем не менее, у меня есть ситуация, когда я вытягивать данные из внешней службы и добавления или обновления существующих значений по первичному ключу (и моих локальных данных для потребителей только для чтения) - уже с использованием AddOrUpdate в производстве для более 6 месяцев и до сих пор никаких проблем.

+6

Пространство имен System.Data.Entity.Migrations содержит классы, связанные с миграциями на основе кода и их конфигурациями. Есть ли причина, по которой мы не должны использовать это в наших репозиториях для нелегального объекта AddOrUpdates? 24 фев. 152015-02-24 20:58:55

+5

Позаботьтесь о методе AddOrUpdate: http://thedatafarm.com/data-access/take-care-with-ef-4-3-addorupdate-method/ 14 май. 152015-05-14 08:43:46


3

Ответ Ладислава был близок, но мне пришлось сделать пару модификаций, чтобы заставить его работать в 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); 
     } 
    } 
[....] 

3

Волшебства происходит при вызове 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(); 
    } 
} 

More about EntityState

Если вы не можете проверить на Id = 0, чтобы определить, является ли это новое юридическое лицо или нет, проверьте answer of Ladislav Mrnka.


1

По моему мнению, стоит сказать, что с недавно выпущенным EntityGraphOperations for Entity Framework Code First вы можете спасти себя от написания повторяющихся кодов для определения состояний всех объектов на графике. Я являюсь автором этого продукта. И я опубликовал его в github, code-project (содержит пошаговую демонстрацию, и образец проекта готов к загрузке) и 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 }); 
     } 
    } 

Это все.


2

Вставьте другое обновление и

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

Я использовал этот подход, но и проверил (после первого или по умолчанию) if (query == null) 27 янв. 182018-01-27 16:40:00