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);
以上将使用 User
和 Roles
导航创建 one-to-many 关系,该导航使用 User_Email
作为外键 属性。
您的代码无法正常工作的原因是
- 使用
HasKey
,您定义了单个 属性 PK 而不是复合 PK。
- 您还没有使用
HasForeignKey
方法指定外键属性,所以EF会为您添加一个影子属性。此外,在您的关系配置中,您仅使用了 Roles
导航。 EF 将尝试使用 User
导航创建另一个关系,有效地在 UserDTO
和 RoleDTO
之间建立 2 个关系。这就是您获得额外列的原因,因为 EF 创建了影子属性,按照惯例用作外键属性。
我正在尝试构建具有一对多关系的代码优先数据模型。
有一个 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
的 classclass 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);
以上将使用 User
和 Roles
导航创建 one-to-many 关系,该导航使用 User_Email
作为外键 属性。
您的代码无法正常工作的原因是
- 使用
HasKey
,您定义了单个 属性 PK 而不是复合 PK。 - 您还没有使用
HasForeignKey
方法指定外键属性,所以EF会为您添加一个影子属性。此外,在您的关系配置中,您仅使用了Roles
导航。 EF 将尝试使用User
导航创建另一个关系,有效地在UserDTO
和RoleDTO
之间建立 2 个关系。这就是您获得额外列的原因,因为 EF 创建了影子属性,按照惯例用作外键属性。