C# 如何通过Fluent API实体框架定义多对多关系?
以下是我的模型:C# 如何通过Fluent API实体框架定义多对多关系?,c#,entity-framework,many-to-many,ef-fluent-api,C#,Entity Framework,Many To Many,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 Lis
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;}
}
我有例外
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.
但是,当我从另一端添加配置时,一切都很好。
i、 e
modelBuilder.Entity
.HasMany(s=>s.url)
.带有多个(s=>s.关键字)
.Map(s=>
{
s、 MapLeftKey(“关键字ID”);
s、 MapRightKey(“UrlId”);
s、 ToTable(“关键字urlmapping”);
});
为什么?。为什么我必须从这两个实体中添加配置,在我读过的地方和许多其他地方,其中一个实体的配置应该可以
当我应该为关系中涉及的两个实体添加配置时,是什么情况
我需要明白这一点。为什么?请提供帮助。在使用Fluent API的多对多映射中,MapLeftKey和MapRightKey中的术语
Left
和Right
可能会被误解,我猜您的问题是由这种误解造成的
有人可能会认为这意味着它们描述了多对多联接表中的“左”和“右”列。如果让EF代码首先基于Fluent映射创建数据库和联接表,则实际情况就是这样
但是,当您创建到现有数据库的映射时,情况并不一定如此
为了用用户
-角色
模型的原型多对多示例来说明这一点,假设您有一个包含用户
、角色
和角色扮演者
表的现有数据库:
现在,您希望将此表架构映射到一个简单模型:
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; }
}
这种映射是错误的,如果你试图把“安娜”变成“营销”的角色
…SaveChanges
将完全抛出您当前遇到的异常。当您捕获随SaveChanges
发送的SQL命令时,原因就会清楚:
exec sp_executesql N'insert[dbo].[RoleUsers]([RoleId],[UserId])
值(@0,@1)
,N'@0 int,@1 int',@0=1,@1=2
因此,EF希望在这里向联接表RoleUsers
中插入一行,其中RoleId
为1
和UserId
为2
,这会导致外键约束冲突,因为Users
表中没有具有UserId
的用户
换句话说,上面的映射将列RoleId
配置为表Users
的外键,将列UserId
配置为表角色的外键。为了纠正映射,我们必须在MapRightKey
中的联接表中使用“left”列名,在MapLeftKey
中使用“right”列:
m.MapLeftKey("UserId");
m.MapRightKey("RoleId");
实际上,通过查看Intellisense的描述,可以更清楚地了解“左”和“右”的真正含义:
MapLeftKey
为左外键配置列的名称最新版本
左外键表示在中指定的导航属性
有很多电话。
MapRightKey
为右外键配置列的名称最新版本
右外键表示在中指定的导航属性
有很多电话。
因此,“Left”和“Right”是指实体在Fluent映射中的显示顺序,而不是连接表中的列顺序。表中的顺序实际上并不重要,您可以在不破坏任何内容的情况下更改它,因为EF发送的INSERT
是一个“扩展的”INSERT
,它还包含列名,而不仅仅是值
也许MapFirstEntityKey
和MapSecondEntityKey
对这些方法名称的选择会更少误导——或者MapSourceEntityKey
和MapTargetEntityKey
这是一篇关于两个字的长文章
如果我的猜测是正确的,它与您的问题有任何关系,那么我会说您的第一个映射不正确,您只需要第二个正确的映射。“有关详细信息,请参阅InnerException”。是吗?对于这个很少有用的异常,了解内部异常非常重要。@Slauma,我已经用内部异常更新了这个问题,但请注意,从两侧添加关系可以使其工作。有趣的问题是,如果只有第二个映射(没有第一个映射)可以使其工作。如果是,我怀疑映射表中的列KeywordId
实际上是表url
的外键,而不是关键字的外键。(只有当这是一个具有手动创建关系的现有数据库时才可能,而不是在您首先使用代码创建数据库时才可能。)否@Slauma,我检查了fk_关系,并且KeywordId映射到KeywordMaster的Id。我不知道为什么会这样;有时它只是工作;有时候就不会了。还有一件事,我有一个静态的单一上下文。我希望这不是所有这些的原因是的,Left
和Right
这两个术语在这里很容易混淆,因为它们似乎表明它们与表中的列顺序有关。也许MapFirstEntityKey
和MapSecondEntityKey
会更清楚地表明它们实际上引用了映射中的实体顺序。当然,如果EF首先创建数据库本身,然后左匹配。但是,如果您手动执行,则顺序可能正好相反。+1用于深入的讨论和解释。我花了大约20个调试堆栈跟踪视图来了解您在一篇长文章中解释的内容。在我的Expirence中,它只是oposit,LeftKey用于WithMany单元格,RightKey用于HasMany单元格。这里还提到:为MapLeftKey和MapRightKey ex命名方法名为MapSourceEntityKey和MapTargetEntityKey
modelBuilder.Entity<KeyWord>
.HasMany(s => s.URLs)
.WithMany(s => s.Keywords)
.Map(s =>
{
s.MapLeftKey("KeywordId");
s.MapRightKey("UrlId");
s.ToTable("KeywordUrlMapping");
});
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; }
}
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");
});
var anna = ctx.Users.Find(1);
var marketing = ctx.Roles.Find(2);
anna.Roles.Add(marketing);
ctx.SaveChanges();
m.MapLeftKey("UserId");
m.MapRightKey("RoleId");