如何在EF6 Code First中创建与枚举相对应的表格?

时间:2016-01-01 15:48:18

标签: c# entity-framework enums ef-code-first entity-framework-6

我已经关注如何处理EF6的Code First中的枚举MSDN。它起作用,假设创建的表中引用枚举数的字段是一个简单的 int

我更喜欢生成第二个表,其值将遵循C#代码中枚举数的定义。因此,我不想仅在MSDN上的示例中获取与 Department 对应的表,而是还希望看到由 Faculty 中的项填充的第二个表。

public enum Faculty { Eng, Math, Eco }     

public partial class Department 
{ 
  [Key] public Guid ID { get; set; } 
  [Required] public Faculty Name { get; set; } 
}

研究这个问题,我偶然发现了一个solution,它建议为枚举创建一个表并通过种子显式填充它。

在我看来,这是一种繁琐的方法,需要自动处理大量工作。毕竟,系统知道构成枚举的实际值。从数据库的角度来看,它仍然是数据行,就像我创建的实体一样,但是从OO方面来说,它并不是真正的数据 - 而是一种可以假设有限的类型(松散表达)已知的州数。

是否填充表格"手动"推荐?

7 个答案:

答案 0 :(得分:90)

由于EF不会自动处理,,这是推荐的方法。

我建议您在文章中进行一些修改。

重命名您的枚举

public enum FacultyEnum { Eng, Math, Eco }

创建一个表示表

的类
public class Faculty
{
    private Faculty(FacultyEnum @enum)
    {
        Id = (int)@enum;
        Name = @enum.ToString();
        Description = @enum.GetEnumDescription();
    }

    protected Faculty() { } //For EF

    [Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
    public int Id { get; set; }

    [Required, MaxLength(100)]
    public string Name { get; set; }

    [MaxLength(100)]
    public string Description { get; set; }

    public static implicit operator Faculty(FacultyEnum @enum) => new Faculty(@enum);

    public static implicit operator FacultyEnum(Faculty faculty) => (FacultyEnum)faculty.Id;
}

您的模型引用了类

public class ExampleClass
{
    public virtual Faculty Faculty { get; set; }
}

创建扩展方法以从枚举和种子值

获取描述
using System;
using System.ComponentModel;
using System.Data.Entity;
using System.Data.Entity.Migrations;
using System.Linq;

public static class Extensions
{
    public static string GetEnumDescription<TEnum>(this TEnum item)
        => item.GetType()
               .GetField(item.ToString())
               .GetCustomAttributes(typeof(DescriptionAttribute), false)
               .Cast<DescriptionAttribute>()
               .FirstOrDefault()?.Description ?? string.Empty;

    public static void SeedEnumValues<T, TEnum>(this IDbSet<T> dbSet, Func<TEnum, T> converter)
        where T : class => Enum.GetValues(typeof(TEnum))
                               .Cast<object>()
                               .Select(value => converter((TEnum)value))
                               .ToList()
                               .ForEach(instance => dbSet.AddOrUpdate(instance));
}

在Configuration.cs

中添加种子
protected override void Seed(Temp.MyClass context)
{
    context.Facultys.SeedEnumValues<Faculty, FacultyEnum>(@enum => @enum);
    context.SaveChanges();
}

在DbContext

中添加枚举表
public class MyClass : DbContext
{
    public DbSet<ExampleClass> Examples { get; set; }
    public DbSet<Faculty> Facultys { get; set; }
}

使用

var example = new ExampleClass();
example.Faculty = FacultyEnum.Eng;

if (example.Faculty == FacultyEnum.Math)
{
    //code
}

要记住

如果您未在Faculty属性中添加虚拟,则必须使用DbSet中的Include方法执行Eager Load

var exampleFromDb = dbContext.Examples.Include(x => x.Faculty).SingleOrDefault(e => e.Id == 1);
if (example.Faculty == FacultyEnum.Math)
{
    //code
}

如果Faculty属性是虚拟的,那么只需使用它

var exampleFromDb = dbContext.Examples.Find(1);
if (example.Faculty == FacultyEnum.Math)
{
    //code
}

答案 1 :(得分:10)

基于@Alberto Monteiro回答我已经创建了泛型类,以防你有几个表。这里的通知是Id是TEnum的类型。以这种方式使用它将提供使用Enum声明属性类型的选项。

public class Question
{
    public QuestionTypeEnum QuestionTypeId { get; set; } // field property

    public QuestionType QuestionType { get; set; } // navigation property
}

默认使用整数枚举,因此db提供程序将使用“int”类型创建字段。

<强> EnumTable.cs

    public class EnumTable<TEnum>
        where TEnum : struct
    {
        public TEnum Id { get; set; }
        public string Name { get; set; }

        protected EnumTable() { }

        public EnumTable(TEnum enumType)
        {
            ExceptionHelpers.ThrowIfNotEnum<TEnum>();

            Id = enumType;
            Name = enumType.ToString();
        }

        public static implicit operator EnumTable<TEnum>(TEnum enumType) => new EnumTable<TEnum>(enumType);
        public static implicit operator TEnum(EnumTable<TEnum> status) => status.Id;
    }

<强> ExceptionHelpers.cs

static class ExceptionHelpers
{
    public static void ThrowIfNotEnum<TEnum>()
        where TEnum : struct
    {
        if (!typeof(TEnum).IsEnum)
        {
            throw new Exception($"Invalid generic method argument of type {typeof(TEnum)}");
        }
    }
}

现在你可以继承EnumTable

public enum QuestionTypeEnum
{
    Closed = 0,
    Open = 1
}

public class QuestionType : EnumTable<QuestionTypeEnum>
{
    public QuestionType(QuestionTypeEnum enumType) : base(enumType)
    {
    }

    public QuestionType() : base() { } // should excplicitly define for EF!
}

种子值

context.QuestionTypes.SeedEnumValues<QuestionType, QuestionTypeEnum>(e => new QuestionType(e));

答案 2 :(得分:5)

另一种可能性,如果你想让你的模型更简单,POCO风格,使用枚举作为一个属性,将按实体框架存储为整数。

然后,如果你想要&#34;枚举表&#34;要在数据库中创建和更新,我建议使用nuget包https://github.com/timabell/ef-enum-to-lookup并在EF Migration种子方法中使用它,例如:

public enum Shape
{
    Square,
    Round
}

public class Foo
{
    public int Id { get; set; }
    public Shape Shape { get; set; }
}

public class MyDbContext : DbContext
{
    public DbSet<Foo> Foos { get; set; }
}

using(var context = new MyDbContext())
{
    var enumToLookup = new EnumToLookup
    {
        TableNamePrefix = string.Empty,
        NameFieldLength = 50,
        UseTransaction = true
    };
    enumToLookup.Apply(context);
}

这将创建&#34; Shape&#34;表格中有两行名为Square和Round,表格中有相关的外键约束&#34; Foo&#34;

答案 3 :(得分:1)

在EF Core中可行的另一种方法(对我来说更简单):

您的枚举

public enum Color
{
    Red = 1,
    Blue = 2,
    Green = 3,
}

数据库表

public class CustomObjectDto
{
    public int ID { get; set; }

    // ... other props

    public Color ColorID { get; set; }
    public ColorDto ColorDto { get; set; }
}

public class ColorDto
{
    public Color ID { get; set; }
    public string Name { get; set; }
}

您的DbContext

public class Db : DbContext
{
    public Db(DbContextOptions<Db> options) : base(options) { }

    public DbSet<CustomObjectDto> CustomObjects { get; set; }
    public DbSet<ColorDto> Colors { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // Seed database with all Colors
        foreach (Color color in Enum.GetValues(typeof(Color)).Cast<Color>())
        {
            ColorDto colorDto = new ColorDto
            {
                ID = color,
                Name = color.ToString(),
            };

            modelBuilder.Entity<ColorDto>().HasData(colorDto);
        }
    }
}

在代码中,我基本上只使用枚举Color(从不使用ColorDto)。但是,在“ CustomObjects”表中包含“ Colors”表和FK仍然很不错,用于SQL查询和视图。

答案 4 :(得分:0)

您应该在: byte声明的前面添加enum

enum MyFieldEnum : byte{
    one = 1,
    two = 2,
    three = 4
}

在数据库中,您应该看到TINYINT并且不需要强制转换!

答案 5 :(得分:0)

阿尔贝托·蒙泰罗(Alberto Monteiro)回答得很好。我必须进行一些调整才能使其与EF core一起使用。

重命名枚举并添加描述修饰符

public enum FacultyEnum 
{
    [Description("English Professor")]
    Eng, 
    [Description("Math Professor")]
    Math, 
    [Description("Economics Professor")]
    Eco 
}

创建一个代表表格的类

public class Faculty
{
    private Faculty(FacultyEnum @enum)
    {
        Id = (int)@enum;
        Name = @enum.ToString();
        Description = @enum.GetEnumDescription();
    }

    protected Faculty() { } //For EF

    [Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
    public int Id { get; set; }

    [Required, MaxLength(100)]
    public string Name { get; set; }

    [MaxLength(100)]
    public string Description { get; set; }

    public static implicit operator Faculty(FacultyEnum @enum) => new Faculty(@enum);

    public static implicit operator FacultyEnum(Faculty faculty) => (FacultyEnum)faculty.Id;
}

您的模型参考了课程

public class ExampleClass
{
    public virtual Faculty Faculty { get; set; }
}

创建扩展方法以从枚举和种子值获取描述

using System;
using System.ComponentModel;
using System.Data.Entity;
using System.Data.Entity.Migrations;
using System.Linq;

public static class Extensions
{
    public static string GetEnumDescription<TEnum>(this TEnum item)
        => item.GetType()
               .GetField(item.ToString())
               .GetCustomAttributes(typeof(DescriptionAttribute), false)
               .Cast<DescriptionAttribute>()
               .FirstOrDefault()?.Description ?? string.Empty;
}

在YourDbContext.cs中添加种子

protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Faculty>().HasData(FacultyEnum.Eng, FacultyEnum.Math, FacultyEnum.Eco);
    }

在您的DbContext中添加枚举表

public class MyClass : DbContext
{
    public DbSet<ExampleClass> Examples { get; set; }
    public DbSet<Faculty> Facultys { get; set; }
}

使用它

var example = new ExampleClass();
example.Faculty = FacultyEnum.Eng;

if (example.Faculty == FacultyEnum.Math)
{
    //code
}

要记住

如果您未在Faculty属性中添加虚拟,则必须使用DbSet中的Include方法进行热切加载

var exampleFromDb = dbContext.Examples.Include(x => x.Faculty).SingleOrDefault(e => e.Id == 1);
if (example.Faculty == FacultyEnum.Math)
{
    //code
}

如果Faculty属性是虚拟的,则只需使用它

var exampleFromDb = dbContext.Examples.Find(1);
if (example.Faculty == FacultyEnum.Math)
{
    //code
}

答案 6 :(得分:0)

优秀@AlbertoMonterio!为了使它与ASP.NET CORE / EF Core一起使用,我对Alberto的解决方案进行了一些调整。

为简洁起见,下面仅显示修改内容:

创建扩展方法以从枚举和种子值获取描述

using System;
using System.ComponentModel;
using System.Data.Entity;
using System.Data.Entity.Migrations;
using System.Linq;
using Microsoft.EntityFrameworkCore; //added
using Microsoft.EntityFrameworkCore.Metadata.Builders; //added

public static class Extensions
{
    //unchanged from alberto answer
    public static string GetEnumDescription<TEnum>(this TEnum item)
        => item.GetType()
               .GetField(item.ToString())
               .GetCustomAttributes(typeof(DescriptionAttribute), false)
               .Cast<DescriptionAttribute>()
               .FirstOrDefault()?.Description ?? string.Empty;

    //changed
    public static void SeedEnumValues<T, TEnum>(this ModelBuilder mb, Func<TEnum, T> converter)
    where T : class => Enum.GetValues(typeof(TEnum))
                           .Cast<object>()
                           .Select(value => converter((TEnum)value))
                           .ToList()
                            .ForEach(instance => mb.Entity<T>().HasData(instance));
}

在Configuration.cs中添加种子

将种子添加到DataContext的OnModelCreating

protected override void OnModelCreating(ModelBuilder builder)
{
    builder.SeedEnumValues<Faculty, EnumEntityRole>(e => e);
}