Ответ 1
Термины Left
и Right
в MapLeftKey
и MapRightKey
в сопоставлении "многие ко многим" с Fluent API могут быть неправильно истолкованы, и я думаю, ваша проблема вызвана этим недоразумением.
Можно подумать, что это означает, что они описывают столбцы, которые являются "левыми" и "правыми" в таблице соединений "многие-ко-многим". Это на самом деле случай, если вы позволите EF Code-First создать базу данных и присоединиться к таблице на основе вашего Fluent-сопоставления.
Но это не обязательно, когда вы создаете сопоставление с существующей базой данных.
Чтобы проиллюстрировать это прототипом примера "много-ко-многим" модели User
- Role
, предположим, что у вас есть существующая база данных с таблицами Users
, Roles
и RoleUsers
:
Теперь вы хотите сопоставить эту схему таблицы с простой моделью:
public class User
{
public User()
{
Roles = new List<Role>();
}
public int UserId { get; set; }
public string UserName { get; set; }
public ICollection<Role> Roles { get; set; }
}
public class Role
{
public int RoleId { get; set; }
public string RoleName { get; set; }
}
И вы добавляете сопоставление Fluent для объекта Users
(вы должны сделать это таким образом, потому что по соглашению вышеприведенная модель будет "один ко многим", и вы не можете начать с стороны сущности Role
, потому что он не имеет коллекции Users
):
modelBuilder.Entity<User>()
.HasMany(u => u.Roles)
.WithMany()
.Map(m =>
{
m.MapLeftKey("RoleId"); // because it is the "left" column, isn't it?
m.MapRightKey("UserId"); // because it is the "right" column, isn't it?
m.ToTable("RoleUsers");
});
Это сопоставление неверно, и если вы попытаетесь поставить "Анна" в роль "Маркетинг"...
var anna = ctx.Users.Find(1);
var marketing = ctx.Roles.Find(2);
anna.Roles.Add(marketing);
ctx.SaveChanges();
... SaveChanges
точно выдает исключение, которое у вас есть. Причина становится очевидной при захвате команды SQL, которая отправляется с помощью SaveChanges
:
exec sp_executesql N'insert [dbo].[RoleUsers]([RoleId], [UserId])
values (@0, @1)
',N'@0 int,@1 int',@0=1,@1=2
Итак, EF хочет вставить здесь строку в таблицу соединений RoleUsers
с RoleId
из 1
и UserId
of 2
, которая вызывает нарушение ограничения внешнего ключа, потому что нет пользователя с UserId
2
в таблице Users
.
Другими словами, приведенное выше сопоставление сконфигурировало столбец RoleId
как внешний ключ в таблице Users
и столбец UserId
в качестве внешнего ключа в таблице Roles
. Чтобы исправить отображение, мы должны использовать "левое" имя столбца в таблице соединений в столбце MapRightKey
и в правом столбце в MapLeftKey
:
m.MapLeftKey("UserId");
m.MapRightKey("RoleId");
Фактически, глядя на Intellisense, описание дает более четкое представление о том, что "Left" и "Right" действительно означает:
MapLeftKey
Настраивает имя столбца (ов) для левого внешнего ключа. The левый внешний ключ представляет свойство навигации, указанное в Вызов HasMany.
MapRightKey
Настраивает имя столбца (ов) для внешнего ключа. The правый внешний ключ представляет свойство навигации, указанное в WithMany.
Итак, "Left" и "Right" относятся к порядку, в котором объекты отображаются в сопоставлении Fluent, а не в порядке столбцов в таблице соединений. Порядок в таблице фактически не имеет значения, вы можете изменить его, не нарушая ничего, потому что INSERT
, отправленный EF, является "расширенным" INSERT
, который также содержит имена столбцов, а не только значения.
Возможно, MapFirstEntityKey
и MapSecondEntityKey
были бы менее вводящим в заблуждение выбором этих имен методов - или, может быть, MapSourceEntityKey
и MapTargetEntityKey
.
Это было длинное сообщение о двух словах.
Если мое предположение верно, что оно вообще имеет какое-либо отношение к вашей проблеме, я бы сказал, что ваше первое сопоставление неверно и вам нужно только второе и правильное сопоставление.