Die Begriffe Left
und Right
in MapLeftKey
und MapRightKey
in dem many-to-many-Mapping mit Fluent API falsch verstanden werden können, und ich denke, das Problem durch dieses Missverständnis verursacht wird.
Man könnte denken, dass dies bedeutet, dass sie die Spalten beschreiben, die in der Viele-zu-Viele-Join-Tabelle "links" und "rechts" sind. Dies ist der Fall, wenn Sie EF Code-First basierend auf Ihrem Fluent-Mapping die Datenbank erstellen und die Tabelle verknüpfen lassen.
Dies ist jedoch nicht unbedingt der Fall, wenn Sie eine Zuordnung zu einer vorhandenen Datenbank erstellen.
Um dies zu veranschaulichen, mit dem prototypischen many-to-many Beispiel eines User
- Role
Modell annimmt, dass Sie eine vorhandene Datenbank mit einer Users
, Roles
und RoleUsers
Tabelle:
Jetzt, Sie wollen dieses Tabellenschemas auf ein einfaches Modell zur Karte:
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; }
}
Und Sie fügen Sie das Fluent Mapping für die 0.123.Einheit (Sie müssen es auf diese Weise tun, denn durch Konvention über das Modell One-to-many sein würde und man kann nicht von der Role
Entität Seite gestartet werden, da es keine Users
Sammlung hat):
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");
});
Diese Zuordnung ist falsch und wenn Sie versuchen, "Anna" in Rolle "Marketing" zu setzen ...
var anna = ctx.Users.Find(1);
var marketing = ctx.Roles.Find(2);
anna.Roles.Add(marketing);
ctx.SaveChanges();
... SaveChanges
werfen wird genau die Ausnahme, die Sie haben werden. Der Grund wird klar, wenn Sie den SQL-Befehl erfassen, die mit SaveChanges
gesendet wird:
exec sp_executesql N'insert [dbo].[RoleUsers]([RoleId], [UserId])
values (@0, @1)
',N'@0 int,@1 int',@0=1,@1=2
So will EF hier eine Zeile in der Join-Tabelle einfügen RoleUsers
mit einem RoleId
von 1
und einem UserId
von 2
welche verursacht die Fremdschlüsseleinschränkungsverletzung, da kein Benutzer mit UserId
2
in der Users
Tabelle vorhanden ist.
Mit anderen Worten, über die Abbildung die Säule RoleId
als Fremdschlüssel der Tabelle Users
und die Spalte UserId
als Fremdschlüssel der Tabelle Roles
konfiguriert hat. Um die Zuordnung haben wir verwenden, um die „linke“ Spaltennamen in der Join-Tabelle in MapRightKey
und die „richtige“ Spalte in MapLeftKey
zu korrigieren:
m.MapLeftKey("UserId");
m.MapRightKey("RoleId");
Eigentlich bei Intellisense suchen die Beschreibung macht deutlicher, was „Left "und "recht" wirklich bedeutet:
MapLeftKey
den Namen der Spalte Konfiguriert (n) für den linken Fremdschlüssel. Der linke Fremdschlüssel stellt die im HasMany-Aufruf angegebene Navigationseigenschaft dar.
MapRightKey
Konfiguriert den Namen der Spalte (n) für den rechten Fremdschlüssel. Der rechte Fremdschlüssel stellt die Navigationseigenschaft dar, die im WithMany-Aufruf angegeben ist.
Also, „Links“ und „Rechts“ auf die Reihenfolge beziehen, in denen die Einheiten in der Fluent Mapping erscheinen, nicht auf die Reihenfolge der Spalten in der Tabelle kommen. Die Reihenfolge in der Tabelle spielt eigentlich keine Rolle, Sie können sie ändern, ohne etwas zu brechen, da die von EF gesendete INSERT
eine "erweiterte" INSERT
ist, die auch die Spaltennamen und nicht nur die Werte enthält.
Vielleicht MapFirstEntityKey
und MapSecondEntityKey
wäre eine weniger irreführende Wahl dieser Methode Namen haben - oder vielleicht MapSourceEntityKey
und MapTargetEntityKey
.
Dies war ein langer Post über zwei Worte.
Wenn meine Vermutung richtig ist, dass es überhaupt etwas mit Ihrem Problem zu tun hat, dann würde ich sagen, dass Ihre erste Zuordnung falsch ist und Sie nur die zweite und korrekte Zuordnung benötigen.
"* Siehe die InnerException für Details *". Hast du? Für diese selten nützliche Ausnahme ist die innere Ausnahme wirklich wichtig zu wissen. – Slauma
@Slauma, Ich habe die Frage mit der inneren Ausnahme aktualisiert, aber beachten Sie, dass das Hinzufügen der Beziehung von beiden Seiten es funktioniert. –
Interessante Frage wäre, wenn * nur * das zweite Mapping (ohne das erste Mapping) funktionieren würde. Wenn ja, würde ich vermuten, dass die Spalte "KeywordId" in der Zuordnungstabelle tatsächlich der Fremdschlüssel für die Tabelle "URLs" und nicht für "Keywords" ist. (Sollte nur möglich sein, wenn es sich um eine vorhandene DB mit manuell angelegten Beziehungen handelt, nicht wenn Sie die DB mit Code-First erstellt haben.) – Slauma