下面是我的模型:
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;}
}
很明显,两个实体都具有多对多关系。 我选择了流利的 api 来告诉 Entity Framework 这种关系,即
modelBuilder.Entity<TMUrl>
.HasMany(s => s.Keywords)
.WithMany(s => s.URLs).Map(s =>
{
s.MapLeftKey("KeywordId");
s.MapRightKey("UrlId");
s.ToTable("KeywordUrlMapping");
});
但是当我这样做的时候
url.Keywords.Add(dbKey); //where url is object of TMUrl,
//dbKey is an existing/new object of Keyword
db.SaveChanges();
我得到异常
An error occurred while saving entities that do not expose foreign key
properties for their relationships....
内部异常:
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.
但是当我也从另一端添加配置时,一切正常。 即
modelBuilder.Entity<KeyWord>
.HasMany(s => s.URLs)
.WithMany(s => s.Keywords)
.Map(s =>
{
s.MapLeftKey("KeywordId");
s.MapRightKey("UrlId");
s.ToTable("KeywordUrlMapping");
});
为什么?为什么我必须从两个实体添加配置,我已经阅读了 here和许多其他地方,其中一个实体的配置应该做。
什么情况下,我应该为关系中涉及的两个实体添加配置?
我需要明白这一点。为什么。请帮忙。
最佳答案
MapLeftKey
和 MapRightKey
中的 Left
和 Right
在 Fluent 的多对多映射中API 可能会被误解,我猜你的问题就是由这种误解引起的。
有人可能认为这意味着他们描述了多对多连接表中“左”和“右”的列。如果您让 EF Code-First 根据您的 Fluent 映射创建数据库和连接表,实际上就是这种情况。
但当您创建到现有数据库的映射时,情况不一定如此。
为了使用 User
-Role
模型的原型(prototype)多对多示例来说明这一点,假设您有一个包含 Users
、Roles
和 RoleUsers
表:
现在,您想要将此表架构映射到一个简单的模型:
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; }
}
然后为 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");
});
此映射是错误的,如果您尝试将“Anna”放入角色“Marketing”...
var anna = ctx.Users.Find(1);
var marketing = ctx.Roles.Find(2);
anna.Roles.Add(marketing);
ctx.SaveChanges();
...SaveChanges
将完全抛出您遇到的异常。当您捕获使用 SaveChanges
发送的 SQL 命令时,原因就很清楚了:
exec sp_executesql N'insert [dbo].[RoleUsers]([RoleId], [UserId])
values (@0, @1)
',N'@0 int,@1 int',@0=1,@1=2
所以,EF 想在这里插入一行到连接表 RoleUsers
中,RoleId
为 1
和 UserId
的 2
导致违反外键约束,因为 Users中没有具有
表。UserId
2
的用户
换句话说,上面的映射配置了RoleId
列作为表Users
的外键,UserId
列作为外键表 角色
的键。为了更正映射,我们必须在 MapRightKey
的连接表中使用“左”列名称,在 MapLeftKey
中使用“右”列:
m.MapLeftKey("UserId");
m.MapRightKey("RoleId");
实际上查看 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.
因此,“左”和“右”是指实体在 Fluent 映射中出现的顺序,而不是指连接表中的列顺序。表中的顺序实际上并不重要,您可以在不破坏任何内容的情况下更改它,因为 EF 发送的 INSERT
是一个“扩展的”INSERT
,其中还包含列名而不仅仅是值(value)观。
也许 MapFirstEntityKey
和 MapSecondEntityKey
是那些方法名称的误导性较小的选择 - 或者 MapSourceEntityKey
和 MapTargetEntityKey
.
这是一篇关于两个词的长篇文章。
如果我的猜测是正确的,它与您的问题有任何关系,那么我会说您的第一个映射不正确,您只需要第二个正确的映射。
关于c# - 如何通过 Fluent API Entity Framework 定义多对多关系?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16490334/