2013-05-10 3 views
31

Unten ist mein Modell:Wie man Viele-zu-Viele-Beziehung durch Fluent API Entity Framework definiert?

public class TMUrl 
{ 
    //many other properties 

    //only property with type Keyword 
    public List<Keyword> Keywords{get;set;} 
} 

public class Keyword 
{ 
    //many other properties 

    //only property with type TMUrl 
    public List<TMUrl> Urls{get;set;} 
} 

So klar, beide Entitäten haben viele-zu-viele-Beziehung. wählte ich fließend api die Entity-Framework über diese Beziehung dh

modelBuilder.Entity<TMUrl> 
       .HasMany(s => s.Keywords) 
       .WithMany(s => s.URLs).Map(s => 
       { 
        s.MapLeftKey("KeywordId"); 
        s.MapRightKey("UrlId"); 
        s.ToTable("KeywordUrlMapping"); 
       }); 

zu sagen, aber wenn ich

url.Keywords.Add(dbKey); //where url is object of TMUrl, 
         //dbKey is an existing/new object of Keyword 
db.SaveChanges(); 

bekomme ich Ausnahme

An error occurred while saving entities that do not expose foreign key 
properties for their relationships.... 

Innerexception:

The INSERT statement conflicted with the FOREIGN KEY constraint 
"KeywordMaster_Keyword". The conflict occurred in database "DbName", 
table "dbo.KeywordMaster", column 'Id'.The statement has been terminated. 

, aber wenn ich Konfiguration auch von der anderen Seite hinzufüge, funktioniert alles gut. heißt

modelBuilder.Entity<KeyWord> 
     .HasMany(s => s.URLs) 
     .WithMany(s => s.Keywords) 
     .Map(s => 
       { 
        s.MapLeftKey("KeywordId"); 
        s.MapRightKey("UrlId"); 
        s.ToTable("KeywordUrlMapping"); 
       }); 

Warum ?. Warum muss ich die Konfiguration von beiden Entitäten hinzufügen, wo ich here und viele andere Orte gelesen habe, sollte die Konfiguration für eine der Entitäten tun.

Was ist der Fall, wenn ich Konfiguration für beide Entitäten hinzufügen sollte, die an der Beziehung beteiligt sind?

Ich muss das verstehen. Warum. Bitte helfen Sie.

+0

"* Siehe die InnerException für Details *". Hast du? Für diese selten nützliche Ausnahme ist die innere Ausnahme wirklich wichtig zu wissen. – Slauma

+0

@Slauma, Ich habe die Frage mit der inneren Ausnahme aktualisiert, aber beachten Sie, dass das Hinzufügen der Beziehung von beiden Seiten es funktioniert. –

+0

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

Antwort

109

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:

Many-to-many database tables

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 UserId2 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.

+9

+1 für die Erklärung von MapLeftKey und MapRightKey – moke

+3

+1 für die tiefe Diskussion und Erklärung. Nahm mich etwa 20 oder so Debug-Stack-Trace-Ansichten, um herauszufinden, was Sie in einem langen Post erklärt. – GoldBishop

+0

wie in meiner expirience, ist es nur die oposit, der LeftKey ist für die WithMany-Zelle und der RightKey ist für die HasMany-Zelle. auch hier erwähnt: https://msdn.microsoft.com/en-us/data/hh134698.aspx – IFink

Verwandte Themen