c# - tutorial - enum entity framework 6




Como criar uma tabela correspondente ao enum no código EF6 primeiro? (4)

Alberto Monteiro respondeu isso muito bem. Eu tive que fazer alguns ajustes para fazê-lo funcionar com o núcleo EF.

Renomeie seu enum e adicione decoradores de descrição

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

Crie uma classe que represente a tabela

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;
}

Seu modelo faz referência à classe

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

Crie um método de extensão para obter uma descrição dos valores enum e seed

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;
}

Adicione a semente em YourDbContext.cs

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

Adicione a tabela de enum no seu DbContext

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

Use-o

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

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

Lembrar

Se você não adicionar virtual na propriedade Faculty, você deve usar o método Include do DbSet para fazer o carregamento ansioso

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

Se a propriedade do corpo docente é virtual, basta usá-lo

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

Eu segui o MSDN sobre como lidar com enumerações no Code First for EF6. Funcionou, como deveria, mas o campo na tabela criada que se refere ao enumerador é um simples int .

Eu preferiria que uma segunda tabela fosse produzida, cujos valores seguiriam a definição do enumerador no código C #. Portanto, em vez de apenas obter uma tabela correspondente ao Departamento no exemplo no MSDN, eu também gostaria de ver uma segunda tabela preenchida pelos itens do Corpo Docente .

public enum Faculty { Eng, Math, Eco }     

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

Pesquisando a questão, me deparei com uma solution , que sugere criar uma tabela para a enumeração e preenchê-la explicitamente pela semeadura.

Parece-me uma abordagem complicada e muito trabalho que deve ser tratado automaticamente. Afinal, o sistema sabe quais valores reais constituem a enumeração. Do ponto de vista do banco de dados, ainda são linhas de dados, assim como as entidades que eu crio, mas do aspecto OO, não são realmente dados - e sim um tipo (livremente expresso) que pode assumir um número de estados finito e conhecido anteriormente.

A abordagem de preencher a tabela "manualmente" é recomendada?


Com base na resposta de @Alberto Monteiro, criei uma classe genérica no caso de você ter várias tabelas. O aviso aqui é que Id é o tipo de TEnum. Usá-lo dessa maneira fornecerá a opção para usar o Enum para declarar o tipo de propriedade.

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

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

Por padrão, Enum usando inteiros, então o provedor db criará um campo com o tipo "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)}");
        }
    }
}

Agora você só pode herdar o 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!
}

Semente os valores

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

Excelente @AlbertoMonterio! Para fazer isso funcionar com o ASP.NET CORE / EF Core, fiz alguns ajustes na solução de Alberto.

Por uma questão de brevidade, apenas as modificações são mostradas abaixo:

Crie um método de extensão para obter uma descrição dos valores enum e seed

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));
}

Adicione a semente no Configuration.cs

Adicionar OnModelCreating ao OnModelCreating do DataContext

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

Outra possibilidade, se você quiser manter seu modelo mais simples, estilo POCO, use o enum como uma propriedade que será armazenada como um inteiro por framework de entidade.

Então, se você quiser que as "tabelas de enumeração" sejam criadas e atualizadas em seu banco de dados, recomendo usar o pacote nuget https://github.com/timabell/ef-enum-to-lookup e usá-lo em uma semente de Migração da EF método por exemplo:

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);
}

Isso criará a tabela "Shape" com 2 linhas chamadas Square e Round, com a restrição de chave estrangeira relevante na tabela "Foo"





entity-framework-6