C# 如何通过 Fluent API Entity Framework 定义多对多关系?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16490334/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 01:08:29  来源:igfitidea点击:

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

c#entity-frameworkmany-to-manyef-fluent-api

提问by Manish Mishra

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 i.e.

很明显,两个实体都有多对多的关系。我选择了 fluent 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:

内部异常:

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. i.e.

但是当我从另一边添加配置时,一切正常。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 hereand 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.

我需要明白这一点。为什么。请帮忙。

采纳答案by Slauma

The terms Leftand Rightin MapLeftKeyand MapRightKeyin the many-to-many mapping with Fluent API can be misunderstood and I guess your problem is caused by this misunderstanding.

条款LeftRightMapLeftKeyMapRightKey在许多一对多映射用流利的API可能被误解,我想你的问题是由这种误解造成的。

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-Rolemodel assume you have an existing database with a Users, Rolesand RoleUserstable:

为了用 a User-Role模型的原型多对多示例来说明这一点,假设您有一个包含Users,RolesRoleUsers表的现有数据库:

Many-to-many database tables

多对多数据库表

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 Usersentity (you must do it this way, because by convention the model above would be one-to-many and you can't start from the Roleentity side because it has no Userscollection):

然后你为Users实体添加了 Fluent 映射(你必须这样做,因为按照惯例,上面的模型是一对多的,你不能从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");
    });

This mapping is wrong and if you try to put "Anna" into role "Marketing"...

此映射是错误的,如果您尝试将“安娜”置于“营销”角色中...

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

anna.Roles.Add(marketing);

ctx.SaveChanges();

...SaveChangeswill throw exactly the exception you are having. The reason becomes clear when you capture the SQL command that is sent with 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

So, EF wants to insert here a row into the join table RoleUserswith a RoleIdof 1and a UserIdof 2which is causing the foreign key constraint violation because there is no user with UserId2in the Userstable.

因此,EF想在这里插入一行到连接表RoleUsersRoleId1UserId2,这是造成外键约束违反,因为没有用户与UserId2Users表中。

In other words, the mapping above has configured the column RoleIdas the foreign key to table Usersand the column UserIdas the foreign key to table Roles. In order to correct the mapping we have to use the "left" column name in the join table in MapRightKeyand the "right" column in MapLeftKey:

换言之,映射上述已配置了该列RoleId作为外键表Users和列UserId作为外键表Roles。为了更正映射,我们必须使用连接表中的“左”列名称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

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.

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.

映射左键

为左外键配置列的名称。左外键表示在 HasMany 调用中指定的导航属性。

映射右键

为正确的外键配置列的名称。右外键表示在 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. The order in the table actually doesn't matter, you can change it without breaking anything because the INSERTsent by EF is an "extended" INSERTthat also contains the column names and not only the values.

因此,“左”和“右”指的是实体在 Fluent 映射中出现的顺序,而不是指连接表中的列顺序。表中的顺序实际上并不重要,您可以在不破坏任何内容的情况下更改它,因为INSERTEF 发送的是一个“扩展” INSERT,它还包含列名而不仅仅是值。

Perhaps MapFirstEntityKeyand MapSecondEntityKeywould have been a less misleading choice of those method names - or maybe MapSourceEntityKeyand MapTargetEntityKey.

也许MapFirstEntityKey并且MapSecondEntityKey对这些方法名称的选择不那么具有误导性 - 或者也许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.

如果我的猜测是正确的,它与您的问题有任何关系,那么我会说您的第一个映射不正确,您只需要第二个正确的映射。