如何通过流畅API实体框架定义许多一对多的关系?(How to define Many-to-Man

2019-09-02 13:06发布

下面是我的模型:

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来讲述这个关系即实体框架

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....

的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.

但是当我从阿瑟赛德藏汉添加配置,一切工作正常。 即

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

为什么?。 为什么我已经从两个实体,在那里我读过添加配置在这里和其他许多地方,配置实体应该做的一个。

什么情况下,我应该都关系中所涉及的实体添加配置?

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

Answer 1:

术语LeftRightMapLeftKeyMapRightKey在许多一对多映射用流利的API可能被误解,我想你的问题是由这种误解造成的。

有人可能会认为,这意味着他们描述的“左”,在许多一对多连接表“正确”的列。 这实际上的情况下,如果你让EF代码首先创建数据库和连接表根据您的流利的映射。

但是,当你创建一个映射到一个现有的数据库,它并不一定如此。

要使用的原型许多一对多的例子来说明这个User - Role模型假设你有一个现有的数据库UsersRolesRoleUsers表:

现在,要映射这个表架构以一个简单的模型:

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实体(你必须做这种方式,因为按照惯例,上述模型是一个一对多的,你不能从一开始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想在这里插入一行到连接表RoleUsersRoleId1UserId2这是造成违反外键约束,因为有没有用户UserId 2Users表。

换言之,上述的映射已配置的列RoleId作为外键表Users和列UserId作为外键表Roles 。 为了纠正我们在连接表中的使用“左”列名的映射MapRightKey和“右”列MapLeftKey

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

其实在看智能感知的描述使得它更清楚什么是“左”和“右”的真正含义:

MapLeftKey

配置列(个),左外键的名称。 左外键代表的的hasMany调用指定的导航属性。

MapRightKey

配置列(个),右外键的名称。 右外键代表在WithMany调用指定的导航属性。

所以,“左”和“右”指的是其中的实体出现在流利的映射的顺序,不要在连接表中的列顺序。 在表中的顺序其实没关系,你可以改变它不会破坏任何东西,因为INSERT通过EF发送是一种“扩展” INSERT还包含列名称而不是只值。

也许MapFirstEntityKeyMapSecondEntityKey本来这些方法名的少误导性的选择-或者MapSourceEntityKeyMapTargetEntityKey

这是一个很长的帖子约两个单词。

如果我的猜测是正确的,那有什么用你的问题都没有,那么我会说,你的第一个映射是不正确的,你只需要在第二和正确的映射。



文章来源: How to define Many-to-Many relationship through Fluent API Entity Framework?