如何在 Entity Framework Core 2.1 中生成枚举的初始值

4
我对EF Core还不熟悉,正在尝试种植一个枚举。
根据数据种植,这个特性是EF Core 2.1的新功能。
我查阅了几个解决方案,包括Blake Mumford的SO解决方案,但由于枚举不是引用类型,所以这对我没用。
我的目标(在迁移的帮助下)是让Category枚举填充到一个名为Category的新SQL表中,并使我的Payment表包含一个引用Category表作为外键的列。
非常感谢任何帮助。谢谢。
public partial class Payment
{
    public int PaymentId { get; set; }
    public DateTime PostedDate { get; set; }
    public string Vendor { get; set; }
    public decimal Amount { get; set; }

    public virtual Category Category { get; set; }
}

public enum Category
{
    Restaurants,
    Groceries,
    [Display(Name = "Home Goods")]
    HomeGoods,
    Entertainment
}

public partial class MyDbContext : DbContext
{
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // Error: The type Category must be a reference type in order to use it as parameter TEntity in the 
        // genric type or method ModelBuilder.Entity<TEntity>()

        modelBuilder.Entity<Category>().HasData(Category.Restaurants, 
                                                Category.Groceries, 
                                                Category.HomeGoods, 
                                                Category.Entertainment);
    }
}
3个回答

4
这是我所做的,希望在你的环境中也能起作用。 环境 项目框架:
  • .NetCore 3.0
Nugets:
  1. EFCore 3.1.2
  2. EFCore.Design 3.1.2
  3. EFCore.Tools 3.1.2
  4. EFCore.Relational 3.1.2
  5. EFCore.SqlServer 3.1.2
实现
  1. Add OnModelCreating

    public partial class MyDbContext : DbContext
    {
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            // Error: The type Category must be a reference type in order to use it as parameter TEntity in the 
            // genric type or method ModelBuilder.Entity<TEntity>()
    
            modelBuilder.Entity<UserRole>().HasData(EnumFunctions.GetModelsFromEnum<UserRole, UserRoleEnum>());
        }
    }
    
  2. Create a Generic Enum Function

    public static class EnumFunctions
    {
        public static IEnumerable<TModel> GetModelsFromEnum<TModel, TEnum>() where TModel : IEnumModel<TModel, TEnum>, new()
        {
            var enums = new List<TModel>();
            foreach (var enumVar in (TEnum[])Enum.GetValues(typeof(TEnum)))
            {
                enums.Add(new TModel
                {
                    Id = enumVar,
                    Name = enumVar.ToString()
                });
            }
    
            return enums;
        }
    }
    
  3. Create an Interface

    public interface IEnumModel<TModel, TModelIdType>
    {
        TModelIdType Id { get; set; }
        string Name { get; set; }
    }
    
  4. Apply the interface to the model

    [Table("UserRoles")]
    public class UserRole : IEnumModel<UserRole, UserRoleEnum>
    {
        [Key]
        public UserRoleEnum Id { get; set; }
        public string Name { get; set; }       
    }
    
  5. Add your migration

    PM> add-migration SeedUserRoleTable
    
你应该会看到已添加迁移。
  1. Update the database with the seeded info

    PM> update-database
    

2

在我的实体文件夹下,我添加了枚举文件夹,它包含以下内容:

public class UserStatus : Enumeration
    {

        public static readonly UserStatus New = new UserStatus(1, "New");
        public static readonly UserStatus UnderReview = new UserStatus(2, "UnderReview");
        public static readonly UserStatus Customer = new UserStatus(3, "Customer");
        public static readonly UserStatus Approved = new UserStatus(4, "Approved");
        public static readonly UserStatus Declined = new UserStatus(5, "Declined");

        public UserStatus(int id, string name)
                    : base(id, name)
        {
        }
    }

在我的DataContext.cs中,我只使用这个来填充数据:
 modelBuilder.Entity<UserStatus>().HasData(Enumeration.GetAll<UserStatus>());

在DataContext.cs中,UserStatus未声明为DbSet。

.NET Core 3.1


1

你不能将枚举类型的值种子到数据库中,因为枚举不是实体类型,因此首先不会进入数据库。换句话说,不会有Categories表,因此在该不存在的表中种子值是没有意义的。

如果你想要实际上将你的类别持久化到数据库中,那么你需要创建一个类似于:

public class Category
{
    public int Id { get; set; }
    public string Name { get; set; }
}

我明白你的意思,但从数据库的角度来看,在代码中拥有一个类别枚举并将其存储为我的付款表中的字符串并不合适。从数据库的角度来看,这应该是一个通过外键引用类别表的ID字段。我找到了一个解决方案(https://dev59.com/pVcP5IYBdhLWcg3whaUD#52498981),在OnModelCreating中使用值转换,这将允许您将类别枚举映射到我的付款表中的ID或字符串,但这仍然无法解决种子数据的问题。 - Pavel
我同意。它很可能应该是一个外键,这就是为什么它应该是一个而不是一个枚举的原因。值转换器对你没有帮助,因为你不能将一个值投影到另一个表中。如果你坚持要把它作为枚举保留下来,那么你只能让它成为一个值列。 - Chris Pratt
但如果我将其作为类而不是枚举,如何进行比较,例如:.Where(p => p.Category == Category.Entertainment)。此外,C#如何知道存储在数据库中的所有这些类别。这似乎是一个变通方法,并且对于应该自然表示为枚举的东西来说并不自然。话虽如此,由于我是EF的新手,最常见的EF解决方案是什么?人们是否大多忽略使用迁移构建DB的方式,只是接受枚举被简单地创建为值列的事实? - Pavel
你想要两件矛盾的事情。枚举的整个意义在于它是一组定义好的、不变的值的枚举。因此,将其持久化到像数据库这样的地方是没有意义的,因为整个目的是允许数据的可变性。如果你想要将其存储到数据库中,那么你就不再有明确定义的值范围,因此无法静态地引用它们。选择一种方法,并停止尝试创建这两者的不可调和的联盟。 - Chris Pratt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接