如何通过Fluent API实体框架定义多对多关系?

2024-03-07

下面是我的模型:

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;}
}

显然,这两个实体具有多对多关系。 我选择 Fluent api 来告诉实体框架这种关系,即

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.

但是当我也从另一端添加配置时,一切正常。 IE。

modelBuilder.Entity<KeyWord>
         .HasMany(s => s.URLs)
         .WithMany(s => s.Keywords)
         .Map(s =>
               {
                  s.MapLeftKey("KeywordId");
                  s.MapRightKey("UrlId");
                  s.ToTable("KeywordUrlMapping");
               });

为什么?。为什么我必须从我读过的两个实体添加配置here http://msdn.microsoft.com/en-us/data/hh134698.aspx和许多其他地方,应该对其中一个实体进行配置。

当我应该为关系中涉及的两个实体添加配置时,会出现什么情况?

我需要理解这一点。为什么。请帮忙。


条款Left and Right in MapLeftKey and MapRightKey在 Fluent API 的多对多映射中可能会被误解,我猜你的问题就是由这种误解引起的。

人们可能会认为这意味着它们描述了多对多连接表中的“左”和“右”列。如果您让 EF Code-First 基于您的 Fluent 映射创建数据库和连接表,实际上就是这种情况。

但当您创建到现有数据库的映射时,情况不一定如此。

为了说明这一点,用一个原型多对多的例子User-Role模型假设您有一个现有数据库Users, Roles and RoleUsers table:

现在,您想要将此表模式映射到一个简单的模型:

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; }
}

然后添加 Fluent 映射Users实体(你必须这样做,因为按照惯例,上面的模型是一对多的,你不能从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");
    });

此映射是错误的,如果您尝试将“安娜”放入“营销”角色......

var anna = ctx.Users.Find(1);
var marketing = ctx.Roles.Find(2);

anna.Roles.Add(marketing);

ctx.SaveChanges();

...SaveChanges将抛出您所遇到的异常。当您捕获发送的 SQL 命令时,原因就清楚了SaveChanges:

exec sp_executesql N'insert [dbo].[RoleUsers]([RoleId], [UserId])
values (@0, @1)
',N'@0 int,@1 int',@0=1,@1=2

因此,EF 希望在连接表中插入一行RoleUsers with a RoleId of 1 and a UserId of 2这导致外键约束冲突,因为没有用户UserId 2 in the Users table.

换句话说,上面的映射已经配置了列RoleId作为表的外键Users和专栏UserId作为表的外键Roles。为了纠正映射,我们必须在连接表中使用“左”列名称MapRightKey和“右”栏MapLeftKey:

        m.MapLeftKey("UserId");
        m.MapRightKey("RoleId");

实际上,查看 Intellisense 的描述可以更清楚地了解“左”和“右”的真正含义:

地图左键

配置左外键的列名称。这 左外键代表在中指定的导航属性 有很多电话。

地图右键

配置右外键的列名称。这 右外键代表在中指定的导航属性 WithMany 调用。

因此,“左”和“右”是指实体在 Fluent 映射中出现的顺序,而不是连接表中的列顺序。表中的顺序实际上并不重要,您可以更改它而不会破坏任何内容,因为INSERTEF 发送的是“扩展”INSERT它还包含列名而不仅仅是值。

Perhaps MapFirstEntityKey and MapSecondEntityKey这些方法名称的选择可能不会那么误导——或者也许MapSourceEntityKey and MapTargetEntityKey.

这是一篇很长的文章,只有两个字。

如果我的猜测是正确的,它与您的问题有任何关系,那么我会说您的第一个映射是不正确的,您只需要第二个正确的映射。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何通过Fluent API实体框架定义多对多关系? 的相关文章

随机推荐