如何在 EF Core Code First 中创建 enum 对应的表?

问题描述 投票:0回答:4

如何将 EF Core 数据库上下文中使用的枚举转换为查找表并添加相关外键?


c# enums entity-framework-core
4个回答
65
投票

您可以在代码中使用枚举,并通过结合使用这两个 EF Core 功能在数据库中使用查找表:

  • 值转换 - 在读/写数据库时将枚举转换为int
  • 数据播种 - 在迁移中将枚举值添加到数据库中

下面是一个数据模型示例:

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


6
投票

这是另一个例子:

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>()
,数据将存储为字符串(在本例中:阴天、晴天或雨天)


0
投票

除了@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;

0
投票

我们需要使用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());
}

}

© www.soinside.com 2019 - 2024. All rights reserved.