[英]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的多对多映射中, MapLeftKey
和MapRightKey
中的Left
和Right
术语可能会被误解,我想您的问题是由这种误解引起的。
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
模型的原型多对多示例说明这一点,假设您有一个现有数据库,该数据库具有Users
, Roles
和RoleUsers
表:
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希望在此处将RoleUsers
表RoleUsers
的行插入,其中RoleId
为1
, UserId
为2
,这导致违反外键约束,因为在Users
表中没有用户UserId
2
的Users
。
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
. 也许MapFirstEntityKey
和MapSecondEntityKey
会是那些方法名称的一个不太引起误解的选择-也许是MapSourceEntityKey
和MapTargetEntityKey
。
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.