Как сохранить объединенные (новые + измененные) отдельные объекты в Entity Framework? - PullRequest
6 голосов
/ 13 декабря 2010

Как правильно и быстро сохранить объединенные новые и измененные отдельные объекты POCO?

Я думал об этих методах:

    private void Method_2(IList<Entity> entities) //detached entities
    {
        //This method is using SELECT to check if entity exist
        using (var context = new ModelContainer())
        {
            foreach (Entity entity in entities)
            {
                var foundEntity = context.CreateObjectSet<Entity>().SingleOrDefault(t => t.Id == entity.Id);
                context.Detach(foundEntity);    //Remove it from ObjectStateManager

                if (foundEntity != null)//It is modified entity
                {
                    context.AttachTo("EntitySet", entity); //Attach our entity
                    context.ObjectStateManager.ChangeObjectState(entity, EntityState.Modified); //We know it exists
                }
                else//It is new entity
                {
                    context.CreateObjectSet<Entity>().AddObject(entity);
                }
            }
            context.SaveChanges();
        }
    }

    private void Method_1(IList<Entity> entities) //detached entities
    {
        //This method doesn't select anything from DB, but i have ta call Savechanges after each object
        using (var context = new ModelContainer())
        {
            foreach (Entity entity in entities)
            {
                try
                {
                    context.AttachTo("EntitySet", entity);
                    context.ObjectStateManager.ChangeObjectState(entity, EntityState.Modified);
                    context.SaveChanges();
                }
                catch (OptimisticConcurrencyException)
                {
                    context.ObjectStateManager.ChangeObjectState(entity, EntityState.Added);
                    context.SaveChanges();
                }
            }
        }
    }

Когда вы работаете в изолированной среде, вы должны знать, какая сущность была добавлена, а какая изменена - вы несете ответственность за сохранение этой информации и ее передачу в ObjectContext.

Ну, я согласен с этим утверждением, если вы оказались в ситуации, когда вам нужно использовать EF-код, подобный этому, в EF, безусловно, что-то не так с вашим решением. Я выбрал неправильный инструмент для этой работы.

Ответы [ 3 ]

4 голосов
/ 13 декабря 2010

Когда вы работаете в изолированной среде, вы должны знать, какая сущность была добавлена, а какая изменена - вы несете ответственность за сохранение этой информации и передачу ее в ObjectContext.

Очень простой способ:

foreach (var entity in entities)
{
  if (entity.Id == 0) // 0 = default value: means new entity
  {
    // Add object
  }
  else
  {
    // Attach object and set state to modified
  }
}

В примере требуется, чтобы у вас был некоторый автоматически сгенерированный первичный ключ (Id) в дБ.

Ваш метод 2 возможен с некоторыми модификациями. Нет необходимости отсоединять сущность при загрузке. Вместо этого используйте ApplyCurrentValues ​​. Подход с загрузкой объекта в первую очередь очень полезен, когда вы решаете работать с графами объектов, а не с одним объектом. Но в случае с графом объектов вы должны выполнить синхронизацию вручную. ApplyCurrentValues ​​работает только для скалярных (не навигационных) свойств. Вы можете попытаться дополнительно оптимизировать свой метод для загрузки необходимых объектов в одну поездку в базу данных вместо загрузки объектов по одному.

Ваш метод 1 - ужасное решение. Использование исключений, возникающих на сервере базы данных, для управления потоком программ - плохой подход.

1 голос
/ 14 декабря 2010

Я согласен с @Ladislav - Method_1 - плохой подход.Позвольте базе данных вызывать исключения, которые перехватываются EF - не пытайтесь проглотить эти исключения самостоятельно.

Вы на правильном пути, используя метод 1.

Вот как я это делаю - каку меня также есть отдельный контекст (POCO, отслеживание изменений отсутствует, ASP.NET MVC).

Интерфейс BLL: (обратите внимание, у меня есть TPT в моей модели, следовательно, дженерики. "Post" является абстрактным)

void Add(Post post);
void Update<TPost>(TPost post) where TPost : Post, new();

Ограничение new() имеет решающее значение - вскоре вы поймете, почему.

Я не покажу, как я делаю "Добавить", потому что это просто, как вы думаете - AddObject(entity);

«Обновление» - сложная часть:

public class GenericRepository<T> : IRepository<T> where T : class
{
   public void Update<T2>(T2 entity) where T2: class, new()
   {
      var stub = new T2(); // create stub, now you see why we need new() constraint

      object entityKey = null;
      // ..snip code to get entity key via attribute on all domain entities
      // once we have key, set on stub.

      // check if entity is already attached..
      ObjectStateEntry entry;
      bool attach;

      if (CurrentContext.ObjectStateManager.TryGetObjectStateEntry(CurrentContext.CreateEntityKey(CurrentContext.GetEntityName<T>(), stub), out entry))
      {
         // Re-attach if necessary.
         attach = entry.State == EntityState.Detached;
      }
      else
      {
         // Attach for first time.
         attach = true;
      }

      if (attach)
         CurrentEntitySet.Attach(stub as T);

      // Update Model. (override stub values attached to graph)
      CurrentContext.ApplyCurrentValues(CurrentContext.GetEntityName<T>(), entity);
   }
}

И это работает для меня.

Что касается ключа сущности, я использовал атрибуты в своих классах домена.Альтернатива (к которой я собираюсь перейти) состоит в том, чтобы все сущности моего домена реализовали интерфейс, который указывает, что все сущности домена должны иметь свойство, называемое EntityKey.Тогда я буду использовать этот интерфейс в моих ограничениях.По сути, мне нужен был динамический способ создания объектов-заглушек в общем хранилище.

Мне лично не нравится идея " проверки идентификатора, если его> 0, то это обновление ».Поскольку я работаю с ASP.NET MVC, если я (или другой разработчик) забудет привязать идентификатор к представлению, он не будет пропущен, поэтому даже если это может быть обновление, потому что идентификатор == 0это будет добавлено.

Я хотел бы быть явным об операциях.Таким образом, я могу выполнить Добавить / Обновить отдельную логику проверки.

0 голосов
/ 13 декабря 2010

Возможно, взгляните на самообследование объектов POCO. ИМХО они идеально подходят для любого сценария, который требует, чтобы сущность была отделена от контекста. Он позаботится обо всем сантехническом коде для вас.

...