【问题标题】:Entity Framework core one to many relationship实体框架核心一对多关系
【发布时间】:2016-01-28 05:45:43
【问题描述】:

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

有一个 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 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);
    }
}

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

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

******************************* * 角色 * ******************************* * User_Email - 字符串/varchar * * 值 - 字符串 /varchar * ******************************* FK - User_Email 到用户表电子邮件列 PK - User_Email,值复合主键

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

谢谢!

【问题讨论】:

  • 现在称为 EF Core,不妨更改标题以反映它

标签: c# one-to-many entity-framework-core


【解决方案1】:

首先,类UserDTO 没有名为User_Email 的属性。您可以使用 EF 在模型中定义阴影属性,但考虑到您希望它在模型中,您应该在 public string User_Email 类上定义属性

一旦您使用 fluent API 在类上定义了上述属性或在模型中定义了影子属性,

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

要将User_Email, Value 定义为复合PK,请在OnModelCreating 中编写以下代码(如果您这样做,则在定义shadow 属性之后)

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

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

User_Email 设置为FK 到UserDTO 表,假设RoleDTO 中的UserDTO UserUserDTO 中的ICollection&lt;RoleDTO&gt; Roles 是此关系的导航。

使用数据注释, 在User_Email 属性上写ForeignKey("User") 或在任一导航上写ForeignKey("User_Email") (User/Roles)。请记住,如果您将 User_Email 作为影子属性,则不能使用它。

使用 Fluent API, 在OnModelCreating 中编写以下代码来完全配置关系

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

上面将使用UserRoles 导航创建一对多关系,其中使用User_Email 作为外键属性。

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

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

【讨论】:

    【解决方案2】:

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

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

    我希望这会有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-05-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-12-01
      • 2021-02-15
      • 1970-01-01
      • 2020-07-08
      相关资源
      最近更新 更多