多对多关系中的复合主键作为阴影 属性

Composite primary key as shadow property in many-to-many relation

我正在使用 .NET Core end Entity Framework core 在两个实体之间建立多对多关系。我已经构建了连接实体来满足关系并基于影子属性的主键,如下所示:

实体用户:

public class User
{
    [Key]
    public int IDUser { get; set; }
    [Required]
    public string Forename { get; set; }

    public List<UserGroup> UsersGroups { get; set; }
}

实体组:

public class Group
{
    [Key]
    public int IDGroup { get; set; }
    [Required]
    public string GroupName { get; set; }

    public List<UserGroup> UsersGroups { get; set; }
}

实体用户组:

public class UserGroup
{
    public Group Group { get; set; }
    public User User { get; set; }
}

数据库上下文 class :

public class DBContext : DbContext
{
    public DBContext(DbContextOptions<DBContext> options)
        : base(options)
    {

    }


    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // shadow property - primary/foreign key
        modelBuilder.Entity<UserGroup>()
            .Property<int>("IDUser");

        // shadow property - primary/foreign key
        modelBuilder.Entity<UserGroup>()
            .Property<int>("IDGroup");

        // composite primary key based on shadow properties
        modelBuilder.Entity<UserGroup>()
            .HasKey( new string[]{ "IDUser", "IDGroup" });

        modelBuilder.Entity<UserGroup>()
            .HasOne(ug => ug.Group)
            .WithMany(g => g.UsersGroups)
            .HasForeignKey(???); //what to do here ?

        modelBuilder.Entity<UserGroup>()
            .HasOne(ug => ug.User)
            .WithMany(u => u.UsersGroups)
            .HasForeignKey(???); // what to do here ?

        base.OnModelCreating(modelBuilder);
    }

    public DbSet<Group> Groups { get; set; }
    public DbSet<User> Users { get; set; }
    public DbSet<UserGroup> UserGroups { get; set; }
}

现在。如何根据影子复合主键在 UserGroup 实体上正确建立外键?我希望这个影子主键同时成为外键。我现在不知道如何引用这个影子主键来制作外键。我用问号标记了我不知道该怎么做的地方。

.HasForeignKey() 在您的实体上声明外键属性。

如果您不想在您的链接实体上使用外键属性(您应该拥有它们),只需省略 .HasForeignKey 声明,EF 将按照惯例使用映射 FK 列。

例如

   protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // shadow property - primary/foreign key
        modelBuilder.Entity<UserGroup>()
            .Property<int>("IDUser");

        // shadow property - primary/foreign key
        modelBuilder.Entity<UserGroup>()
            .Property<int>("IDGroup");

        // composite primary key based on shadow properties
        modelBuilder.Entity<UserGroup>()
            .HasKey(new string[] { "IDUser", "IDGroup" });

        modelBuilder.Entity<UserGroup>()
            .HasOne(ug => ug.Group)
            .WithMany(g => g.UsersGroups);
        //.HasForeignKey(???); //what to do here ?

        modelBuilder.Entity<UserGroup>()
            .HasOne(ug => ug.User)
            .WithMany(u => u.UsersGroups);
            //.HasForeignKey(???); // what to do here ?

        base.OnModelCreating(modelBuilder);
    }

生成

CREATE TABLE [UserGroups] (
    [IDUser] int NOT NULL,
    [IDGroup] int NOT NULL,
    CONSTRAINT [PK_UserGroups] PRIMARY KEY ([IDUser], [IDGroup]),
    CONSTRAINT [FK_UserGroups_Groups_IDGroup] FOREIGN KEY ([IDGroup]) REFERENCES [Groups] ([IDGroup]) ON DELETE CASCADE,
    CONSTRAINT [FK_UserGroups_Users_IDUser] FOREIGN KEY ([IDUser]) REFERENCES [Users] ([IDUser]) ON DELETE CASCADE
);

您正在尝试创建多对多连接 table 而未定义任何标量属性,并且您正在使用影子 属性 配置连接 table。对于 EF fluent API,您必须引用阴影 属性,您需要使用基于字符串的方法。由于缺少支持 CLR 属性 lambda 表达式不起作用。

在您的例子中,"what to do here" 部分只是使用 属性 的字符串名称。 例如

modelBuilder.Entity<UserGroup>()
    .HasOne(ug => ug.Group)
    .WithMany(g => g.UsersGroups)
    .HasForeignKey("IDGroup"); //what to do here ?

其他关系也一样。当您想要将影子 属性 配置为您的外键 属性 时,这是通用机制。此外,在 HasForeignKey 中配置影子 属性 不需要您提前定义影子 属性,因为 EF 可以根据关系主体侧的属性推断类型。尽管对于 HasKey 您仍然需要声明影子属性,因为 EF 不知道类型。 (正如您在示例中所做的那样)

EF Core 也有计算 FK 的约定 属性。约定之一是使用 属性 作为 FK,如果它与主体 属性 同名。在上述特殊情况下,由于您的主体侧主键 属性 被命名为 IDGroup,这与您尝试配置的外键相同,因此 EF 将按照约定自动使用它。这意味着您可以忽略配置您的关系(如@David 建议的那样)。此外,由于 EF 基于导航发现关系,您可以从您的应用程序中完全删除以下代码,它将创建相同的模型。

modelBuilder.Entity<UserGroup>()
    .HasOne(ug => ug.Group)
    .WithMany(g => g.UsersGroups);
//.HasForeignKey(???); //what to do here ?

modelBuilder.Entity<UserGroup>()
    .HasOne(ug => ug.User)
    .WithMany(u => u.UsersGroups);
    //.HasForeignKey(???); // what to do here ?