Как сохранить обновленную коллекцию «многие ко многим» на отдельном объекте POCO Entity Framework 4.1 - PullRequest
2 голосов
/ 07 декабря 2011

Последние несколько дней я пытаюсь правильно обновить свои объекты POCO. Точнее, это коллекции отношений «многие ко многим».

У меня есть три таблицы базы данных: Автор - 1..n - AuthorBooks - n..1 - Книги.

Переводится на два объекта POCO: Объект Author с коллекцией Books и объект Book с коллекцией Author .

Case

Когда у меня есть один активный DbContext, извлекаем сущность Book, добавляем Author и вызываем SaveChanges (), изменения правильно отправляются в базу данных. Пока все хорошо.

Однако у меня есть настольное приложение с ограниченным временем жизни DbContext, как показано в фрагментах кода ниже.

public Book GetBook(int id)
{
   using (var context = new LibariesContext())
   {
      return context.Books
         .Include(b => b.Authors)
         .AsNoTracking()
         .Single(b => b.BookId == id);
   }
}

public Author GetAuthor(int id)
{
   using (var context = new LibariesContext())
   {
      return context.Authors
         .AsNoTracking()
         .Single(a => a.AuthorId == id);
   }
}

Упрощенный пример различных моих методов бизнес-логики, объединяет их:

public void BusinessLogicMethods()
{
   Book book = GetBook(id: 1);
   Author author = GetAuthor(id: 1);

   book.Name = "New book title";
   book.Authors.Add(author);
   SaveBook(book);
}

public void SaveBook(Book book)
{
   using (var context = new LibariesContext())
   {
      context.Entry(book).State = System.Data.EntityState.Modified;
      context.SaveChanges();
   }
}

К сожалению, единственное, что здесь действительно сохраняется, это название книги. Новый автор не был сохранен, и не было брошено исключение.

Вопросы

  • Какой лучший способ сохранить коллекцию отсоединенного объекта?
  • Есть ли какое-нибудь решение этой проблемы?

Ответы [ 2 ]

4 голосов
/ 07 декабря 2011

Я тоже новичок в EF 4.1, и если я правильно понимаю ваш вопрос, я думаю, что столкнулся с этой чепухой на выходных. Попробовав каждый подход под солнцем, чтобы получить записи для обновления, я нашел здесь мантру на SO (я больше не могу ее найти) и превратил ее в общий метод расширения. Итак, теперь после строки:

book.Authors.Add(author);

Я бы добавил строку:

context.UpdateManyToMany(book, b => b.Authors)

Возможно, вам придется реструктурировать свой код, чтобы это произошло.

Во всяком случае ... вот метод расширения, который я написал. Дайте мне знать, если это работает (без гарантий !!!)

    public static void UpdateManyToMany<TSingle, TMany>(
        this DbContext ctx,
        TSingle localItem,
        Func<TSingle, ICollection<TMany>> collectionSelector)
        where TSingle : class
        where TMany : class
    {
        DbSet<TSingle> localItemDbSet = ctx.Set(typeof(TSingle)).Cast<TSingle>();
        DbSet<TMany> manyItemDbSet = ctx.Set(typeof(TMany)).Cast<TMany>();

        ObjectContext objectContext = ((IObjectContextAdapter) ctx).ObjectContext;
        ObjectSet<TSingle> tempSet = objectContext.CreateObjectSet<TSingle>();
        IEnumerable<string> localItemKeyNames = tempSet.EntitySet.ElementType.KeyMembers.Select(k => k.Name);

        var localItemKeysArray = localItemKeyNames.Select(kn => typeof(TSingle).GetProperty(kn).GetValue(localItem, null));

        localItemDbSet.Load();

        TSingle dbVerOfLocalItem = localItemDbSet.Find(localItemKeysArray.ToArray());
        IEnumerable<TMany> localCol = collectionSelector(localItem)?? Enumerable.Empty<TMany>();
        ICollection<TMany> dbColl = collectionSelector(dbVerOfLocalItem);
        dbColl.Clear();

        ObjectSet<TMany> tempSet1 = objectContext.CreateObjectSet<TMany>();
        IEnumerable<string> collectionKeyNames = tempSet1.EntitySet.ElementType.KeyMembers.Select(k => k.Name);

        var selectedDbCats = localCol
            .Select(c => collectionKeyNames.Select(kn => typeof (TMany).GetProperty(kn).GetValue(c, null)).ToArray())
            .Select(manyItemDbSet.Find);
        foreach (TMany xx in selectedDbCats)
        {
            dbColl.Add(xx);
        }
        ctx.Entry(dbVerOfLocalItem).CurrentValues.SetValues(localItem);
    }
1 голос
/ 13 сентября 2013

Я сталкивался с этим вопросом, когда пытался решить ту же проблему.В конце я выбрал другой подход, который, кажется, работает.Мне пришлось в конечном итоге выставить свойство «состояние» для сущностей, и контекст вызова установил состояние сущностей в графе объектов.

Это уменьшает накладные расходы на стороне веб-службы / контекста данных, чтобы иметьопределить, что изменилось, учитывая, что график может быть заполнен любым количеством запросов.Есть также пакет NuGet под названием GraphDiff, который также может работать для вас (подробности в ссылке ниже).

В любом случае, полная информация здесь: http://sanderstechnology.com/2013/solving-the-detached-many-to-many-problem-with-the-entity-framework/12505/

...