当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《如何在EF6CodeFirst中创建与枚举对应的表?》经验,为你挑选了3个好方法。

我已经跟踪MSDN如何处理EF6的Code First中的枚举.它起作用,但是在创建的表中引用枚举器的字段是一个简单的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; } 
}

在研究这个问题时,我偶然发现了一个解决方案,它建议为枚举创建一个表并通过种子显式填充它.

在我看来,这是一种繁琐的方法和许多应该自动处理的工作.毕竟,系统知道构成枚举的实际值.从数据库的角度来看,它仍然是数据行,就像我创建的实体一样,但从OO方面来说,它实际上并不是一个数据 - 而是一种类型(松散表达),它可以假设一个有限的和预先知道的状态数.

是否建议"手动"填充表格的方法?



1> Alberto Mont..:

由于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(this TEnum item)
        => item.GetType()
               .GetField(item.ToString())
               .GetCustomAttributes(typeof(DescriptionAttribute), false)
               .Cast()
               .FirstOrDefault()?.Description ?? string.Empty;

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


在Configuration.cs中添加种子

protected override void Seed(Temp.MyClass context)
{
    context.Facultys.SeedEnumValues(@enum => @enum);
    context.SaveChanges();
}

在DbContext中添加枚举表

public class MyClass : DbContext
{
    public DbSet Examples { get; set; }
    public DbSet 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
}


你很清楚.而*年份=新年{State = States.Happy};*也是你.Yey!今年我的第一个书呆子笑话!
@KonradViltersten`eventStream.ReceiveJoke + = joke => Console.WriteLine($"相当不错的笑话:{笑话},谢谢!!!");
@RitwikSen枚举是C#中的保留字,要将此单词用作变量/方法/类名,必须使用sufix @.
措辞很好的回答.仅努力+1.现在,据我所知,EF不会通过其设计自动处理枚举,我想知道它是否只是设计师做出的选择,或者是否存在逻辑,技术或可能是语义原因.**如果**它会以我们懒惰的方式处理它,那会不会导致概念上的悖论呢?我看到它可以通过一个值为整数和两个字符串的表来解析 - 名称和描述.我是否过分简化了这个问题?
@KonradViltersten我认为一切都取决于.所以EF试图满足大多数人的最普遍需求.我从来没有为Enum创建一个表,只需使用"int"符合我的需要.恕我直言我认为EF设计选择了这一点,因为大多数人都以这种方式使用并且具有更好的性能和DB Size优化.此外,我不认为这会导致概念上的悖论,如果有一个有效的理由来创建另一个表,那就得到它.我清楚了吗?顺便说一下新年快乐!

2> unsafePtr..:

基于@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
        where TEnum : struct
    {
        public TEnum Id { get; set; }
        public string Name { get; set; }

        protected EnumTable() { }

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

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

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

ExceptionHelpers.cs

static class ExceptionHelpers
{
    public static void ThrowIfNotEnum()
        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
{
    public QuestionType(QuestionTypeEnum enumType) : base(enumType)
    {
    }

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

种子化的价值观

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


这当然打破了隐含的运营商.为了解决这个问题,我制作了"EnumTable"摘要,并从中删除了隐式运算符.然后我将它们添加到派生类中.如果开发人员忘记添加隐式运算符,那么当他们尝试分配类型的引用或尝试在配置中设置种子时,他们将获得有关缺少隐式转换的编译器错误.我觉得这个可以接受.

3> Michael..:

另一种可能性,如果你想让你的模型更简单,就像POCO样式一样,那就是使用enum有一个属性,它将按实体框架存储为整数.

然后,如果您希望在数据库中创建和更新"枚举表",我建议使用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 Foos { get; set; }
}

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

这将创建"Shape"表,其中包含名为Square和Round的2行,并在表"Foo"中使用相关的外键约束

推荐阅读
殉情放开那只小兔子
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有