我正在尝试使用 EF Core Fluent API 创建一对多映射。
我有这3张桌子:
User
Role
这些 C# 类代表这些表:
public class User
{
public string Id { get; set; }
public string Email { get; set; }
}
public class Role
{
public string Id { get; set; }
public string RoleName { get; set; }
}
public class UserRoleMapping
{
public string Id { get; set; }
public string UserId { get; set; }
public string RoleId { get; set; }
}
使用
IEntityTypeConfiguration<TEntity> where TEntity : class
Fluent api 配置已为 User
和 Role
表完成,但我无法为 UserRoleMapping
表进行 Fluent API 配置。
当我尝试跑步时:
var role = await uow.RoleRepository
.GetAsync(_ => _.Active,
_ => _.OrderByDescending(s => s.CreatedDate),
s => s.UserRole.User);
我的期望是我应该能够使用该查询,当获取角色时还需要获取关联的用户信息,这是我无法执行的。
下面是配置
internal class UserConfig : BaseEntityConfig<UserEntity>
{
private readonly string TableName = DatabaseTableName.User;
public override void Configure(EntityTypeBuilder<UserEntity> builder)
{
builder
.ToTable(TableName);
builder.HasKey(_ => _.Id);
builder.Property(_ => _.Email).HasColumnName("email_address");
builder
.HasOne(_ => _.UserRole)
.WithOne(_=>_.User)
.HasForeignKey<UserRoleEntity>(_ => _.UserId);
//builder.HasOne(_ => _.UserRole)
// .WithOne(_ => _.User)
// .HasForeignKey<UserRoleEntity>(_ => _.RoleId);
//builder.Property(_ => _.UserRole).HasColumnName("UserRole");
base.Configure(builder);
}
}
角色配置
internal class RoleConfig : BaseEntityConfig<RoleEntity>
{
private readonly string TableName = DatabaseTableName.Role;
public override void Configure(EntityTypeBuilder<RoleEntity> builder)
{
builder
.ToTable(TableName);
builder.HasKey(_ => _.Id);
builder.Property(_ => _.RoleName).HasColumnName("name");
builder.HasOne(_ => _.UserRole)
.WithOne(_ => _.Role)
.HasForeignKey<UserRoleEntity>(_ => _.RoleId);
base.Configure(builder);
}
}
这是多对多关系,而不是一对多。通常,您会取消 UserRole 表上的“Id”,并使用基于 UserId + RoleId 的复合 PK。您的配置试图建立一对一的关系。 (
HasOne().WithOne()
) 一对多会看到一个 User 有一个 RoleId,其中一个“角色”与多个“用户”相关联。这不太可能是您正在寻找的关系与多对多的关系。这些用户属于这些角色。所以一个用户可以有多个角色,一个角色可以有多个用户。
public class User
{
public string Id { get; set; }
public string Email { get; set; }
public virtual ICollection<Role> Roles { get; } = [];
}
public class Role
{
public string Id { get; set; }
public string RoleName { get; set; }
public virtual ICollection<User> Users { get; } = [];
}
然后配置时:
// 用户EntityTypeConfig
builder
.HasMany(_ => _.Roles)
.WithMany(_=>_.Users)
.UsingEntity("UserRole",
l => l.HasOne(typeof(Role))
.WithMany()
.HasForeignKey("RoleId")
.HasPrincipalKey(nameof(Role.Id)),
r => r.HasOne(typeof(User))
.WithMany()
.HasForeignKey("UserId")
.HasPrincipalKey(nameof(User.Id)),
j => j.HasKey("UserId", "RoleId"));
这会在没有 UserRole 实体的情况下配置它,但也可以使用定义的实体类或通过
Dictionary
进行设置。文档中概述了一些选项。 (https://learn.microsoft.com/en-us/ef/core/modeling/relationships/many-to-many)
如果您确实希望通过 UserRole 建立关系,例如,如果您希望为用户和角色之间关系的每个实例添加一些属性到 UserRole,那么可以在关系中显式设置它,但更多的是作为多个-一对多来模仿多对多:
public class User
{
public string Id { get; set; }
public string Email { get; set; }
public virtual ICollection<UserRole> UserRoles { get; } = [];
}
public class Role
{
public string Id { get; set; }
public string RoleName { get; set; }
public virtual ICollection<UserRole> UserRoles { get; } = [];
}
// User EntityTypeConfig
builder
.HasMany(_ => _.UserRoles)
.WithOne(_=> _.User)
.HasForeignKey(_ => _.UserId);
// Role EntityTypeConfig
builder
.HasMany(_ => _.UserRoles)
.WithOne(_=> _.Role)
.HasForeignKey(_ => _.RoleId);
// UserRole EntityTypeConfig
builder.HasKey(_ => new { _.UserId, _.RoleId } ); // or _.Id if you want a dedicated Id column.
使用显式实体的缺点是从角色到用户或从用户到角色不太方便。您需要通过 UserRoles。即
role.UserRoles.Select(_ => _.User);
而不是:
role.Users;
通常仅当您需要额外的属性时才需要映射关联实体,例如,如果您想记录用户与角色关联的日期时间。 UserRole 将具有 CreatedDateTime,因此我们需要映射 UserRole 才能访问此属性。 (与创建用户时用户的 CreatedDateTime 或创建角色时角色的 CreatedDateTime 相比。)