Man*_*hra 32 c# many-to-many entity-framework ef-fluent-api
以下是我的模型:
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;}
}
Run Code Online (Sandbox Code Playgroud)
很明显,两个实体都有多对多的关系.我选择了流畅的api告诉实体框架关于这种关系,即
modelBuilder.Entity<TMUrl>
.HasMany(s => s.Keywords)
.WithMany(s => s.URLs).Map(s =>
{
s.MapLeftKey("KeywordId");
s.MapRightKey("UrlId");
s.ToTable("KeywordUrlMapping");
});
Run Code Online (Sandbox Code Playgroud)
但是当我这样做的时候
url.Keywords.Add(dbKey); //where url is object of TMUrl,
//dbKey is an existing/new object of Keyword
db.SaveChanges();
Run Code Online (Sandbox Code Playgroud)
我得到例外
An error occurred while saving entities that do not expose foreign key
properties for their relationships....
Run Code Online (Sandbox Code Playgroud)
的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.
Run Code Online (Sandbox Code Playgroud)
但是当我从其他地方添加配置时,一切正常.即
modelBuilder.Entity<KeyWord>
.HasMany(s => s.URLs)
.WithMany(s => s.Keywords)
.Map(s =>
{
s.MapLeftKey("KeywordId");
s.MapRightKey("UrlId");
s.ToTable("KeywordUrlMapping");
});
Run Code Online (Sandbox Code Playgroud)
为什么?.为什么我要从两个实体添加配置,我在这里阅读的地方和许多其他地方,其中一个实体的配置应该这样做.
是什么情况,我应该为这两个关系中涉及的实体添加配置?
我需要了解这一点.为什么.请帮忙.
Sla*_*uma 114
条款Left
和Right
中MapLeftKey
和MapRightKey
在许多一对多映射用流利的API可能被误解,我想你的问题是由这种误解造成的.
有人可能认为这意味着他们描述了多对多连接表中"左"和"右"的列.如果您让EF Code-First根据您的Fluent映射创建数据库并连接表,实际上就是这种情况.
但是,当您创建到现有数据库的映射时,情况并不一定如此.
要使用的的原型许多一对多的例子说明这一点User
- Role
模型假设你有一个现有的数据库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; }
}
Run Code Online (Sandbox Code Playgroud)
并为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");
});
Run Code Online (Sandbox Code Playgroud)
这种映射是错误的,如果你试图将"Anna"放入"营销"角色......
var anna = ctx.Users.Find(1);
var marketing = ctx.Roles.Find(2);
anna.Roles.Add(marketing);
ctx.SaveChanges();
Run Code Online (Sandbox Code Playgroud)
... SaveChanges
将完全抛出你所拥有的例外.当您捕获使用以下命令发送的SQL命令时,原因就变得清晰了SaveChanges
:
exec sp_executesql N'insert [dbo].[RoleUsers]([RoleId], [UserId])
values (@0, @1)
',N'@0 int,@1 int',@0=1,@1=2
Run Code Online (Sandbox Code Playgroud)
因此,EF想在这里插入一行到连接表RoleUsers
用RoleId
的1
和UserId
的2
,这是造成外键约束违反,因为没有用户与UserId
2
在Users
表中.
换言之,映射上述已配置了该列RoleId
作为外键表Users
和列UserId
作为外键表Roles
.为了纠正映射,我们必须使用连接表中的"左"列名称MapRightKey
和"右"列中的MapLeftKey
:
m.MapLeftKey("UserId");
m.MapRightKey("RoleId");
Run Code Online (Sandbox Code Playgroud)
实际上看Intellisense时,描述更清楚"左"和"右"的含义是什么:
MapLeftKey
配置左外键的列名称.左外键表示HasMany调用中指定的导航属性.
MapRightKey
配置正确外键的列名称.右外键表示WithMany调用中指定的导航属性.
因此,"左"和"右"指的是实体在Fluent映射中出现的顺序,而不是连接表中的列顺序.表中的顺序实际上并不重要,您可以更改它而不会破坏任何内容,因为INSERT
EF发送的是"扩展" INSERT
,它还包含列名而不仅是值.
也许MapFirstEntityKey
并且MapSecondEntityKey
本来是一个不那么误导性的选择那些方法名称 - 或者也许MapSourceEntityKey
和MapTargetEntityKey
.
这是一篇关于两个词的长篇文章.
如果我的猜测是正确的,它与你的问题有任何关系,那么我会说你的第一个映射是不正确的,你只需要第二个正确的映射.