EF Core与EF不是完全一样的,官方文档列出了详细的差异比较,可以查阅:https://docs.microsoft.com/zh-cn/ef/efcore-and-ef6/。
EF Core支持Code First模式生成数据库。这里以权限管理中的用户-角色-菜单的关系为例,演示一下EF Core中如何通过手动编写Fluent API来配置多对多的关系。
测试环境:VS2022 / .NET 6.0 / EF Core 6.0
假设我们已经设计好了数据库,并建立了主外键关联。如下图所示,一个用户可以对应多个角色,一个角色可以对应多个菜单。
我们首先要准备好上面的表对应的类:
用户类:
查看代码
/// <summary>
/// 用户
/// </summary>
public class User
{
/// <summary>
/// GUID
/// </summary>
[Key]
public string? No { get; set; }
/// <summary>
/// 用户Id
/// </summary>
public string? Id { get; set; }
/// <summary>
/// 用户名
/// </summary>
public string? UserName { get; set; }
/// <summary>
/// 密码
/// </summary>
public string? PassWord { get; set; }
/// <summary>
/// 姓名
/// </summary>
public string? Name { get; set; }
/// <summary>
/// 性别
/// </summary>
public string? Sex { get; set; }
/// <summary>
/// 部门
/// </summary>
public string? DeptId { get; set; }
/// <summary>
/// 工号
/// </summary>
public string? JobNumber { get; set; }
/// <summary>
/// 职位
/// </summary>
public string? Position { get; set; }
/// <summary>
/// 标识
/// </summary>
public int Flag { get; set; }
/// <summary>
/// 最后登录时间
/// </summary>
public DateTime LastLoginTime { get; set; }
/// <summary>
/// 备注
/// </summary>
public string? Remark { get; set; }
/// <summary>
/// 一个用户可以对应多个角色
/// </summary>
public virtual ICollection<Role> Role { get; set; }
}
角色类:
查看代码
/// <summary>
/// 角色
/// </summary>
public class Role
{
/// <summary>
/// GUID
/// </summary>
[Key]
public string? No { get; set; }
/// <summary>
/// 角色Id
/// </summary>
public string? Id { get; set; }
/// <summary>
/// 角色名
/// </summary>
public string? Name { get; set; }
/// <summary>
/// 状态标识
/// </summary>
public int Flag { get; set; }
/// <summary>
/// 角色备注
/// </summary>
public string? Remark { get; set; }
/// <summary>
/// 一个角色对应多个菜单
/// </summary>
public virtual ICollection<Function>? Function { get; set; }
/// <summary>
/// 一个角色对应多个用户
/// </summary>
public virtual ICollection<User>? User { get; set; }
}
菜单类:
查看代码
/// <summary>
/// 功能菜单
/// </summary>
public class Function
{
/// <summary>
/// 功能编号
/// </summary>
public string? Id { get; set; }
/// <summary>
/// 功能名称
/// </summary>
public string? Name { get; set; }
/// <summary>
/// 父级编号
/// </summary>
public string? ParentId { get; set; }
/// <summary>
/// 链接地址
/// </summary>
public string? Url { get; set; }
/// <summary>
/// 链接顺序
/// </summary>
public int Order { get; set; }
/// <summary>
/// 菜单级别
/// </summary>
public int Type { get; set; }
/// <summary>
/// 菜单标志
/// </summary>
public int Flag { get; set; }
/// <summary>
/// 菜单说明
/// </summary>
public string? Remark { get; set; }
/// <summary>
/// 一个菜单可以被多个角色引用
/// </summary>
public virtual ICollection<Role>? Role { get; set; }
}
然后需要创建一个EF上下文来操作数据库:
public class MyDbContext : DbContext
{
public MyDbContext()
{
}
public MyDbContext(DbContextOptions<MyDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
//modelBuilder.Entity<User>()
// .HasMany(o => o.Role)
// .WithMany(o => o.User)
// .UsingEntity<UserRole>(
// o => o.HasOne(o => o.Role).WithMany().HasForeignKey(o => o.Role_No),
// o => o.HasOne(o => o.User).WithMany().HasForeignKey(o => o.User_No),
// o => o.HasKey(t => new { t.User_No, t.Role_No })
// );
modelBuilder.Entity<User>()
.HasMany(o => o.Role)
.WithMany(o => o.User)
.UsingEntity<Dictionary<string, object>>("UserRole",
o => o.HasOne<Role>().WithMany().HasForeignKey("Role_No"),
o => o.HasOne<User>().WithMany().HasForeignKey("User_No"),
o => o.ToTable("UserRole"));
modelBuilder.Entity<Role>()
.HasMany(o => o.Function)
.WithMany(o => o.Role)
.UsingEntity<Dictionary<string, object>>("RoleFunction",
o => o.HasOne<Function>().WithMany().HasForeignKey("Function_Id"),
o => o.HasOne<Role>().WithMany().HasForeignKey("Role_No"),
o => o.ToTable("RoleFunction"));
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
base.OnConfiguring(optionsBuilder);
optionsBuilder.UseSqlServer("server=xxx.xxx.xxx.xxx;database=数据库名称;uid=数据库账号;pwd=数据库密码;");
}
public DbSet<User> User { get; set; }
public DbSet<Role> Role { get; set; }
public DbSet<Function> Function { get; set; }
}
上述代码中的注释部分需要创建一个UserRole中间表对应的类:
[Table("UserRole")]
public class UserRole
{
public string User_No { get; set; }
public string Role_No { get; set; }
[NotMapped]
public User User { get; set; }
[NotMapped]
public Role Role { get; set; }
}
而下面的Dictionary的方式则不需要创建这个类。
Fluent API方式手动配置好关系之后,就可以使用了:
using (var db = new MyDbContext())
{
//当前用户对应多个角色
List<Role> roles = db.User
.Include(o => o.Role)
.ThenInclude(o => o.Function)
.FirstOrDefault(t => t.UserName.Equals("admin"))
.Role
.ToList();
}
通过Include来预先加载关联的Role,再通过ThenInclude实现预先加载多级关联。查询角色对应的菜单也是类似的方式。