Entity Framework 核心一对多关系

Entity Framework core one to many relationship

我正在尝试构建具有一对多关系的代码优先数据模型。

有一个 UserDTO 和一个 RoleDTO 模型,User 包含一对多角色。

RoleDTO 的一些简化代码是:

internal class RoleDTO
{
    public UserDTO User { get; set; }

    public string Value { get; set; }

    public RoleDTO()
    {

    }
}

以及简化的 UserDTO:

internal class UserDTO
{
    public string Email { get; set; }

    public string FullName { get; set; }

    public string UserName { get; set; }

    public virtual ICollection<RoleDTO> Roles { get; set; }

    public UserDTO()
    {

    }
}

最后是继承自 DBContext

的 class
class StorageContext : DbContext
{
    public DbSet<UserDTO> Users { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer(@"My_Connection_String");
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<UserDTO>().HasKey(entity => entity.Email);
        modelBuilder.Entity<UserDTO>().HasMany(entity => entity.Roles);

        modelBuilder.Entity<RoleDTO>().HasOne<UserDTO>().WithMany(x => x.Roles);
        modelBuilder.Entity<RoleDTO>().HasKey(x => x.Value);
    }
}

当我 运行 我的添加迁移并且我没有数据库或迁移文件夹时(即使我 运行 它在有一些列之后)仍然创建角色 table引用用户电子邮件地址的额外列。

我的角色 table 只想要 2 列,一列是用户的电子邮件地址,是用户电子邮件 PK 的 FK,另一列是与外键组合的字符串值在用户 table 中创建一个复合主键。下图:

*******************************
* Role                        *
*******************************
* User_Email - string/varchar *
* Value - string /varchar     *
*******************************
FK - User_Email to User table Email column
PK - User_Email, Value compound primary key

有谁知道如何使用流畅的 API 或数据注释来实现这一点?

谢谢!

我不确定你想用这个实现什么,但在 ORM 中使用复合键可能很复杂,而且是一个不可预测的领域table。所以我建议不要使用复合键,除非你有充分的理由。

我建议不要在任何地方使用电子邮件地址作为 PK。使用生成的唯一 Int32/Int64 甚至 Guid 作为主键(这将在您的角色 table 中反映为外键,一旦您设置了关系)并在您的电子邮件列上放置一个索引(在您的具有唯一约束的用户 table)。您可以在迁移脚本中添加索引。至少通过这种方式,如果电子邮件地址发生变化(如果这是您想要的),您可以保持身份。

希望对您有所帮助。

首先,classUserDTO并没有属性命名为User_Email。您可以使用 EF 在模型中定义阴影 属性,但考虑到您希望它在模型中,您应该在 class public string User_Email[=42= 上定义 属性 ]

一旦您在 class 上定义了上述 属性 或使用流畅的 API、

在模型中定义了影子 属性

要定义复合主键,需要使用HasKey流畅API。您不能使用数据注释来定义复合主键。

要将User_Email, Value定义为复合PK,在OnModelCreating中写入以下代码(如果你这样做的话,在定义阴影属性之后)

modelBuilder.Entity<UserDTO>().HasKey(e => new { e.User_Email, e.Value });

要为关系指定外键 属性,您可以在 fk property/navigation 上使用 ForeignKeyAttribute,也可以使用 HasForeignKey fluent API。

User_Email设置为FK到UserDTO table,假设UserDTO User in RoleDTO & ICollection<RoleDTO> Roles in UserDTO 是这种关系的导航。

有了数据标注, 在 User_Email 属性 上写 ForeignKey("User") 或在任一导航 (User/Roles) 上写 ForeignKey("User_Email")。请记住,如果您将 User_Email 作为影子 属性.

,则不能使用它

流利 API, 在 OnModelCreating 中编写以下代码以完全配置关系

modelBuilder.Entity<RoleDTO>().HasOne(e => e.User).WithMany(e => e.Roles).HasForeignKey(e => e.User_Email);

以上将使用 UserRoles 导航创建 one-to-many 关系,该导航使用 User_Email 作为外键 属性。

您的代码无法正常工作的原因是

  1. 使用 HasKey,您定义了单个 属性 PK 而不是复合 PK。
  2. 您还没有使用HasForeignKey方法指定外键属性,所以EF会为您添加一个影子属性。此外,在您的关系配置中,您仅使用了 Roles 导航。 EF 将尝试使用 User 导航创建另一个关系,有效地在 UserDTORoleDTO 之间建立 2 个关系。这就是您获得额外列的原因,因为 EF 创建了影子属性,按照惯例用作外键属性。