Как вы вставляете или обновляете таблицы "много ко многим" в .net entity Framework
-
19-09-2019 - |
Вопрос
Кажется, что это должно быть совершенно очевидно, но что-то в entity framework сбивает меня с толку, и я не могу заставить это работать.
Проще говоря, у меня есть три таблицы, где значения Id являются столбцами идентификаторов:Пользователи (userId, имя пользователя) Категории (CategoryID, CategoryName) Объединяемый (userId, CategoryID) составной.
В конструкторе сущностей (это .net 4.0), когда я импортирую эти таблицы, как и ожидалось, таблица объединения не отображается, но пользователи и Категории показывают связь.Следующий код:
var _context = new MyContext();
var myUser = new User();
myUser.UserName = "joe";
var myCategory = new Category();
myCategory.CategoryName = "friends";
_context.Users.AddObject(myUser);
myUser.Categories.Add(myCategory);
var saved = _context.SaveChanges();
Возвращает ошибку (хотя в базу данных ничего не было добавлено):
An item with the same key has already been added.
Если я добавлю следующее перед сохранением:
_context.Categories.AddObject(myCategory);
myCategory.Users.Add(myUser);
Я получаю ту же ошибку, и в базе данных ничего не сохраняется.Если я сохраню объекты myUser и myCategory перед попыткой связать их, они оба сохранятся, но при втором сохранении выдается ошибка, при этом в таблицу объединения ничего не добавляется:
Cannot insert the value NULL into column 'UserId', table '...dbo.JoinTable'; column does not allow nulls. INSERT fails. The statement has been terminated.
Я явно не в состоянии понять, сколько отношений ко многим вставлено.Что я упускаю из виду?
Решение
Вам действительно нужно вызвать SaveChanges() после добавления объектов User и Category в базу данных, а затем установить вашу связь между ними.
Однако реальной проблемой здесь является второе исключение, которое вы перечислили.Если вы посмотрите на SqlProfiler или профилировщик ADO.NET в отладчике, вы увидите, что во время второго вызова SaveChanges это выглядит примерно так:
insert [dbo].[JoinTable]([UserId]) values (@0) select [CategoryId] from
[dbo].[JoinTable] where @@ROWCOUNT > 0 and [UserId] = @0 and [CategoryId] = scope_identity()
Очевидно, что это не сработает, если вы правильно запрограммировали свою объединяемую таблицу (составной PK для обоих столбцов).
Если я посмотрю на хранилище EntityModel через Model Browser, это покажет, что столбец CategoryID внутри JoinTable действительно имеет значение StoreGeneratedPattern Identity, в то время как userId имеет значение None.Почему EF сделал это на этапе генерации, когда присутствовал составной PK, мне непонятно.Я отправлю сообщение об ошибке в MS, однако в то же время вы можете вручную отредактировать файл edmx / ssdl после генерации, чтобы удалить спецификатор идентификатора.Найдите строку StoreGeneratedPattern="Identity" под тегом Property тега EntityType для вашей объединяемой таблицы и удалите ее:
Изменение:
<Property Name="CategoryId" Type="int" Nullable="false" StoreGeneratedPattern="Identity" />
Для:
<Property Name="CategoryId" Type="int" Nullable="false" />
Затем, когда вы запустите свой код, вы получите гораздо лучший запрос insert (и больше никаких исключений!).:
insert [dbo].[JoinTable]([UserId], [CategoryId]) values (@0, @1)
Другие советы
Способ, которым я это сделал, заключается в том, чтобы сначала сгенерировать действительный объект категории с ключом объекта.
Category myCategory = _context.Categories.First(i => i.CategoryID == categoryIDToUse);
Или вы можете попытаться создать объект как заглушку, чтобы сохранить попадание в базу данных:
Category myCategory = new Category{CategoryID = categoryIDToUse };
Затем добавьте этот объект в набор объектов (CategorySet) в ObjectContext, используя метод AttachTo (возможно, вы захотите проверить, прикреплен ли он уже).Затем вы можете добавить Категорию в свой пользовательский объект, используя метод Add.Что - то вроде этого:
myUser.Categories.Add(myCategory);
Вызовите SaveChanges().У меня это сработало.
Когда новый родительский элемент добавляется в Контекст, состояние всего объекта в дереве объектов изменяется на Добавленный, и, следовательно, EF пытается сохранить все такие объекты как новую запись.
Перейдите по этой ссылке:http://nileshhirapra.blogspot.in/2012/03/entity-framework-insert-operation-with.html