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");