简体   繁体   English

如何通过Fluent API实体框架定义多对多关系?

[英]How to define Many-to-Many relationship through Fluent API Entity Framework?

Below is my model: 下面是我的模型:

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 clearly, both the entities have many-to-many relationship. 很明显,两个实体都具有多对多关系。 I chose fluent api to tell the entity-framework about this relationship ie 我选择流利的api来告诉实体框架这种关系,即

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

but when I do 但是当我这样做

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

I get exception 我有例外

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

InnerException: 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.

but when I add Configuration from the otherside aswell, everything works fine. 但是当我从另一端添加Configuration时,一切正常。 ie

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

Why?. 为什么?。 Why I've to add configuration from both the entities, where I've read here and many other places, configuration for one of the entities should do. 为什么我必须从两个实体(在这里和其他很多地方都读过)都添加配置,所以应该为其中一个实体配置配置。

What is the case, when I should add configuration for both of the entities involved in the relationship? 在这种情况下,应该为关系中涉及的两个实体添加配置?

I need to understand this. 我需要明白这一点。 Why. 为什么。 Please help. 请帮忙。

The terms Left and Right in MapLeftKey and MapRightKey in the many-to-many mapping with Fluent API can be misunderstood and I guess your problem is caused by this misunderstanding. 在Fluent API的多对多映射中, MapLeftKeyMapRightKey中的LeftRight术语可能会被误解,我想您的问题是由这种误解引起的。

One might think that it means they describe the columns that are "left" and "right" in the many-to-many join table. 可能有人认为这意味着它们描述了多对多联接表中“左”和“右”的列。 That's actually the case if you let EF Code-First create the database and join table based on your Fluent mapping. 如果您让EF Code-First创建基于Fluent映射的数据库并联接表,实际上就是这种情况。

But it's not necessarily the case when you create a mapping to an existing database. 但是,当您创建到现有数据库的映射时,情况不一定如此。

To illustrate this with the prototypic many-to-many example of a User - Role model assume you have an existing database with a Users , Roles and RoleUsers table: 为了用User - Role模型的原型多对多示例说明这一点,假设您有一个现有数据库,该数据库具有UsersRolesRoleUsers表:

多对多数据库表

Now, you want to map this table schema to a simple model: 现在,您想将此表模式映射到一个简单的模型:

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; }
}

And you add the Fluent mapping for the Users entity (you must do it this way, because by convention the model above would be one-to-many and you can't start from the Role entity side because it has no Users collection): 然后为Users实体添加Fluent映射(您必须采用这种方式,因为按照惯例,上述模型将是一对多的,并且由于它没有Users集合,因此无法从Role实体开始):

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");
    });

This mapping is wrong and if you try to put "Anna" into role "Marketing"... 此映射是错误的,如果您尝试将“ Anna”放入“市场营销”角色...

var anna = ctx.Users.Find(1);
var marketing = ctx.Roles.Find(2);

anna.Roles.Add(marketing);

ctx.SaveChanges();

... SaveChanges will throw exactly the exception you are having. ... SaveChanges将完全抛出您所拥有的异常。 The reason becomes clear when you capture the SQL command that is sent with SaveChanges : 当您捕获随SaveChanges发送的SQL命令时,原因就很清楚了:

exec sp_executesql N'insert [dbo].[RoleUsers]([RoleId], [UserId])
values (@0, @1)
',N'@0 int,@1 int',@0=1,@1=2

So, EF wants to insert here a row into the join table RoleUsers with a RoleId of 1 and a UserId of 2 which is causing the foreign key constraint violation because there is no user with UserId 2 in the Users table. 因此,EF希望在此处将RoleUsersRoleUsers的行插入,其中RoleId1UserId2 ,这导致违反外键约束,因为在Users表中没有用户UserId 2Users

In other words, the mapping above has configured the column RoleId as the foreign key to table Users and the column UserId as the foreign key to table Roles . 换句话说,上面的映射已将RoleId列配置为表Users的外键,将UserId列配置为表Roles的外键。 In order to correct the mapping we have to use the "left" column name in the join table in MapRightKey and the "right" column in MapLeftKey : 为了纠正我们必须使用映射“左”列名在连接表MapRightKey和“右”列MapLeftKey

        m.MapLeftKey("UserId");
        m.MapRightKey("RoleId");

Actually looking at Intellisense the description makes it clearer what "Left" and "Right" really mean: 实际上,通过查看Intellisense的描述,可以更清楚地了解“左”和“右”的真正含义:

MapLeftKey MapLeftKey

Configures the name of the column(s) for the left foreign key. 配置左外键的列名。 The left foreign key represents the navigation property specified in the HasMany call. 左外键代表在HasMany调用中指定的导航属性。

MapRightKey MapRightKey

Configures the name of the column(s) for the right foreign key. 为右外键配置列的名称。 The right foreign key represents the navigation property specified in the WithMany call. 右外键代表WithMany调用中指定的导航属性。

So, "Left" and "Right" refer to the order in which the entities appear in the Fluent mapping, not to the column order in the join table. 因此,“左”和“右”是指实体在Fluent映射中出现的顺序,而不是联接表中的列顺序。 The order in the table actually doesn't matter, you can change it without breaking anything because the INSERT sent by EF is an "extended" INSERT that also contains the column names and not only the values. 表中的顺序实际上并不重要,您可以在不中断任何内容的情况下进行更改,因为EF发送的INSERT是“扩展的” INSERT ,它不仅包含列名,而且还包含值。

Perhaps MapFirstEntityKey and MapSecondEntityKey would have been a less misleading choice of those method names - or maybe MapSourceEntityKey and MapTargetEntityKey . 也许MapFirstEntityKeyMapSecondEntityKey会是那些方法名称的一个不太引起误解的选择-也许是MapSourceEntityKeyMapTargetEntityKey

This was a long post about two words. 这是一篇长篇文章,大约两个字。

If my guess is right that it has anything to do with your problem at all then I would say that your first mapping is incorrect and that you only need the second and correct mapping. 如果我的猜测完全与您的问题有关,那么我会说您的第一个映射不正确,而您只需要第二个正确的映射即可。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM