Entity Framework 4.1: сохранение отношения «многие ко многим» снова сохраняет существующие дочерние объекты в БД - PullRequest
0 голосов
/ 21 ноября 2011

У меня очень неприятная проблема (для меня в любом случае):

У меня есть простой сценарий, в котором у меня есть объект Product и объект CombinedProduct. Между ними существует много-много связей, поэтому Продукт может принадлежать нескольким Комбинированным продуктам, а Комбинированный продукт может содержать много продуктов.

Сначала я создаю продукт и сохраняю его в базе данных. Позже я создаю CombinedProduct и добавляю этот продукт. Когда я пытаюсь сохранить этот CombinedProduct, сущность продукта снова добавляется в базу данных с помощью Entity Framework вместо простого добавления отношения ... Это действительно сводит меня с ума.

Я уже пытался снова прикрепить продукт к контексту перед сохранением, но Entity жалуется, что у него уже есть продукт с тем же ключом ...

Ниже вы найдете код для всего этого (упрощенный и выделенный код):

Сущность продукта

Public Class SingleProduct
  Property SingleProductId As Integer
  Property CombinedProducts As ICollection(Of CombinedProduct)
End Class

CombinedProduct

Public Class CombinedProduct
  Public Sub New()
    Me.Products = New HashSet(Of SingleProduct)()
  End Sub

  Property CombinedProductId As Integer
  Property Products As ICollection(Of SingleProduct)
End Class

Определение отношения многих ко многим

Protected Overrides Sub OnModelCreating(modelBuilder As DbModelBuilder)
   modelBuilder.Entity(Of CombinedProduct)().
                        HasMany(Function(c) c.Products).
                        WithMany(Function(p) p.CombinedProducts).
                        Map(Sub(m)
                                m.ToTable("CombinedProductSingleProducts")
                                m.MapLeftKey("SingleProductId")
                                m.MapRightKey("CombinedProductId")
                            End Sub)
End Sub

Код, используемый для сохранения

Using myDataContext As New DataContext
  myDataContext.CombinedProducts.Add(product)
  myDataContext.SaveChanges()
End Using

Уже пытались подключиться перед сохранением, но это не сработало

For Each prd In product.Products
   If myDataContext.Entry(prd).State = EntityState.Detached Then
      myDataContext.SingleProducts.Attach(prd)
   End If
Next

Одно «решение», которое я нашел, было правильным перед сохранением, очисткой списка продуктов и повторным получением продуктов из базы данных и добавлением их в CombinedProduct, но вряд ли это решение.

Надеюсь, кто-нибудь может мне помочь, это сводит меня с ума! (Я использую Entity Framework 4.1, с Code First)

Редактировать

Добавление продукта : Это делается в собственном текстовом формате в другой форме:

Using myDataContext As New DataContext
    myDataContext.SingleProducts.Add(singleProduct)
    myDataContext.SaveChanges()
End Using

Создание комбинированного продукта :

Dim myCombinedProduct = New CombinedProduct
myCombinedProduct.Products.Add(product)

Продукт, который я добавляю, сначала снова выбирается в собственном текстовом формате:

Using myDataContext As New DataContext
    Return myDataContext.Products.FirstOrDefault(Function(p) p.ProductId = id)
End Using

Редактировать

Полная история в надежде быть более понятной:

У меня есть приложение winforms с двумя формами: одна для управления продуктами и одна для управления комбинированными продуктами. Приложение N-уровня (пользовательский уровень, бизнес-уровень и уровень данных).

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

Другая форма комбинированного продукта:

  1. при загрузке формы я извлекаю все продукты из базы данных, просматривая бизнес и данные. Функция для извлечения продуктов в слое данных имеет свой собственный DataContext (используя блок). Эта функция возвращает iEnumerable Products.
  2. Найденные продукты добавляются в ряд комбинированных списков в качестве объектов.
  3. Вы выбираете продукты, которые хотите добавить в комбинированный продукт, выбирая их
  4. при сохранении я создаю новый объект CombinedProduct в пользовательском слое, извлекаю объекты продукта из комбинированных списков и добавляю их в новый объект CombinedProduct
  5. Я отправляю объект CombinedProduct на бизнес-уровень, где выполняю ряд бизнес-правил
  6. Если все в порядке, комбинированный продукт отправляется на уровень данных, где я пытаюсь снова сохранить его в своем собственном текстовом тексте (using-block).

Итак, у меня есть несколько DataContexts, поскольку они живут и умирают в слое данных.

Надеюсь, это прояснит ситуацию.

Ответы [ 2 ]

1 голос
/ 21 ноября 2011

Я точно не понимаю, в каком порядке вы делаете, какие шаги, но правильный порядок должен быть:

Если все происходит в одном контексте:

Using myDataContext As New DataContext
    Dim product = myDataContext.Products.FirstOrDefault(Function(p) p.ProductId = id)

    Dim myCombinedProduct = New CombinedProduct
    myCombinedProduct.Products.Add(product)

    myDataContext.CombinedProducts.Add(myCombinedProduct)
End Using

Загрузка product присоединяется к контексту и избегает дублирования.Это должно произойти до того, как вы добавите myCombinedProduct в контекст.

Если вы загрузите product в другом контексте:

Using myDataContext As New DataContext
    myDataContext.Products.Attach(product)

    Dim myCombinedProduct = New CombinedProduct
    myCombinedProduct.Products.Add(product)

    myDataContext.CombinedProducts.Add(myCombinedProduct)
End Using

Вы должны прикрепитьproduct к новому контексту, снова до , к которому вы добавляете myCombinedProduct к контексту.

Редактирование

Если ваш новый myCombinedProduct включая коллекцию продуктов входит в слой данных в отдельном состоянии, следующее должно работать:

Using myDataContext As New DataContext
    For Each prd In myCombinedProduct.Products
        myDataContext.SingleProducts.Attach(prd)
    Next

    myDataContext.CombinedProducts.Add(myCombinedProduct)
End Using
0 голосов
/ 21 ноября 2011

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

Мне никогда не приходилось связывать / отсоединять сущности, но я видел, что большинство людей сталкивалось с проблемами при этом.

Кроме того, во многих необходимо добавить обе сущности в контекст, отношения просто заполняют объединяющую таблицу

Глядя на обсуждение, похоже, что у вас есть хранилище для каждого типа сущности, и вы получаете объекты с помощью этих методов хранилища. Если это так, то IMO этот проект имеет проблему, так как объектные отношения хранятся в общем контексте. В вашем случае все эти репозитории должны иметь одинаковые dbcontexts. Вы можете подумать, что обращение к базе данных имеет некоторые проблемы с производительностью, но вы обращаетесь к БД, даже когда присоединяете.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...