如何将 EF Core 数据库上下文中使用的枚举转换为查找表并添加相关外键?
您可以在代码中使用枚举,并通过结合使用这两个 EF Core 功能在数据库中使用查找表:
下面是一个数据模型示例:
public class Wine
{
public int WineId { get; set; }
public string Name { get; set; }
public WineVariantId WineVariantId { get; set; }
public WineVariant WineVariant { get; set; }
}
public enum WineVariantId : int
{
Red = 0,
White = 1,
Rose = 2
}
public class WineVariant
{
public WineVariantId WineVariantId { get; set; }
public string Name { get; set; }
public List<Wine> Wines { get; set; }
}
此处
DbContext
您可以在其中配置值转换和数据播种:
public class WineContext : DbContext
{
public DbSet<Wine> Wines { get; set; }
public DbSet<WineVariant> WineVariants { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlite("Data Source=wines.db");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder
.Entity<Wine>()
.Property(e => e.WineVariantId)
.HasConversion<int>();
modelBuilder
.Entity<WineVariant>()
.Property(e => e.WineVariantId)
.HasConversion<int>();
modelBuilder
.Entity<WineVariant>().HasData(
Enum.GetValues(typeof(WineVariantId))
.Cast<WineVariantId>()
.Select(e => new WineVariant()
{
WineVariantId = e,
Name = e.ToString()
})
);
}
}
然后您可以在代码中使用枚举值,如下所示:
db.Wines.Add(new Wine
{
Name = "Gutturnio",
WineVariantId = WineVariantId.Red,
});
db.Wines.Add(new Wine
{
Name = "Ortrugo",
WineVariantId = WineVariantId.White,
});
这是您的数据库将包含的内容:
我将完整的示例发布为要点:https://gist.github.com/paolofulgoni/825bef5cd6cd92c4f9bbf33f603af4ff
这是另一个例子:
public class Weather {
public int Id { get; init; }
public WeatherType Type { get; init; }
}
public enum WeatherType {
Cloudy = 1,
Sunny = 2,
Rainy = 3,
}
您可以在单独的类中添加
HasConversion
,如下所示:
public class WeatherEntityTypeConfiguration : IEntityTypeConfiguration<Weather>
{
public void Configure(EntityTypeBuilder<Weather> builder)
{
builder.ToTable("Weather").HasKey(k => k.Id);
builder.Property(p => p.Id).IsRequired();
builder.Property(p => p.Type).HasConversion<int>().IsRequired();
// builder.Property(p => p.Type).HasConversion<string>().IsRequired();
}
}
注意:如果您使用
HasConversion<int>()
,数据将作为integer
存储在数据库中,但如果您使用HasConversion<string>()
,数据将存储为字符串(在本例中:阴天、晴天或雨天)
除了@PaoloFulgoni之外,如果您想要与枚举建立多对多关系,即您想要许多用户角色或葡萄酒变体并使用枚举,而且您不能将其存储为标记,因为您需要了解没有源代码的角色/权限(在数据库端)。
TLDR ;) 您必须创建一个连接表,其中包含有关谁拥有什么特权(或角色,如果您想要的话)的信息。
有一个包含权限列表的用户表,一个包含权限定义(即 ID、名称)的权限表。以及一个以用户和权限为关键的连接表。如果存在针对此用户/权限组合的条目,则意味着该用户具有此权限/角色。
代码:
//for enum
public enum UserPrivilegeId : int
{
AddProject = 0,
ModifyProject = 1,
DeleteProject = 2,
AddUser = 3,
ModifyUser = 4,
DeleteUser = 5
}
//User class
public record User
{
public User()
{
Privileges = new HashSet<Privilege>();
}
public int Id { get; set; }
public string Username { get; set; }
public string PasswordHash { get; set; }
public virtual ICollection<Privilege> Privileges { get; set; }
public virtual List<UserPrivilege> UserPrivileges { get; set; }
}
//Privilege Class
public record Privilege //note record is IMPORTANT here, because this forces it to compare by value, if you want to *use a class*, then make sure to override GetHashCode and Equals
{
public Privilege()
{
Users = new HashSet<User>();
}
public Privilege(UserPrivilegeId privilegeId, string privilegeName)
{
PrivilegeId = privilegeId;
PrivilegeName = privilegeName;
Users = new HashSet<User>();
}
[Key]
public UserPrivilegeId PrivilegeId { get; set; }
public string PrivilegeName { get; set; }
public virtual ICollection<User> Users { get; set; }
public virtual List<UserPrivilege> UserPrivileges { get; set; }
}
//and finally the UserPrivilege join class
public record UserPrivilege
{
public UserPrivilegeId PrivilageId { get; set; }
public Privilege Privilage { get; set; }
public int UserId { get; set; }
public User User { get; set; }
}
//The set-up in dbContext
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Privilege>()
.HasKey(p => p.PrivilegeId);
modelBuilder.Entity<Privilege>()
.Property(p => p.PrivilegeId)
.HasConversion<int>();
modelBuilder.Entity<User>()
.HasMany(user => user.Privileges)
.WithMany(privilege => privilege.Users)
.UsingEntity<UserPrivilege>(
j => j
.HasOne(up => up.Privilage)
.WithMany(u => u.UserPrivileges)
.HasForeignKey(up => up.PrivilageId),
j => j
.HasOne(up => up.User)
.WithMany(p => p.UserPrivileges)
.HasForeignKey(up => up.UserId),
j =>
{
j.Property(u => u.PrivilageId).HasConversion<int>();
j.HasKey(u => new { u.PrivilageId, u.UserId });
});
//this adds definitions of privileges to the table
modelBuilder.Entity<Privilege>()
.HasData(
Enum.GetValues(typeof(UserPrivilegeId))
.Cast<UserPrivilegeId>()
.Select(p => new Privilege(p, p.ToString())));
base.OnModelCreating(modelBuilder);
}
通过在
IsActive
上创建一个带有布尔值的包装器来使用它,如下所示:
public class UserPrivelegesDTO
{
public UserPrivelegesDTO(UserPrivilegeId privilege, bool isActive)
{
this.PrivilegeId = privilege;
this.PrivilegeName = privilege.ToString();
this.IsActive = isActive;
}
public UserPrivilegeId PrivilegeId { get; set; }
public string PrivilegeName { get; set; }
public bool IsActive { get; set; }
}
如果您想从
List<Privileges>
转换为 List<UserPrivilegeDTO>
,您可以
return await _context.Privileges.OrderBy(x => x.PrivilegeId).ToListAsync(cancellationToken);
要转换回
List<Privileges>
,只需
var privileges = _userPrivilegesViewModel.Privileges.Where(x => x.IsActive).Select(x => new Privilege(x.PrivilegeId, x.PrivilegeName));
如果你想检查用户是否有权限
var user = _context.Users.Include(x => x.Privileges).FirstAsync(x => x.Id == 1);
if (request.Editor.Privileges.Any(p => p.PrivilegeId == UserPrivilegeId.ModifyUser))
return true;
当您想要更新权限时
var PrivilegeChangeUser = await
_context.Users
.Include(user => user.Privileges)
.Include(user => user.UserPrivileges)
.FirstOrDefaultAsync(user => user.Id == request.UserId);
//**NOTE**you *need* to include the join table i.e. UserPrivileges in order replace the privileges, if you do not include it EF will try to add the privileges which already exist :(
//To update the privileges from an IEnumerable<UserPrivilegeIdEnum>
//first get the privileges objects and add that to users
var AllPrivileges =
await _context.Privileges
.Include(x => x.UserPrivileges)
.Include(x => x.Users)
.Where(x =>
request.Privileges
.Contains(x.PrivilegeId)
).ToListAsync(cancellationToken);
PrivilegeChangeUser.Privileges = AllPrivileges;
我们需要使用NuGet包SpatialFocus.EntityFrameworkCore.Extensions 安装包 SpatialFocus.EntityFrameworkCore.Extensions
公共类 YourDBContext :DbContext {
public YourDBContext(DbContextOptions<YourDBContext> options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
public DbSet<ModelClass> YourModeClass{ get; set; }
base.OnModelCreating(modelBuilder);
modelBuilder.ConfigureEnumLookup(
EnumLookupOptions.Default
.Singularize()
.UseNumberAsIdentifier());
}
}