framework first español enum c# entity-framework enums ef-code-first entity-framework-6

first - enum c# int



¿Cómo crear una tabla correspondiente a enum en EF6 Code First? (7)

Alberto Monteiro respondió esto muy bien. Tuve que hacer algunos ajustes para que funcione con EF core.

Cambie el nombre de su enumeración y agregue decoradores de descripción

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

Crea una clase que represente la tabla

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

Su modelo hace referencia a la clase

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

Cree un método de extensión para obtener una descripción de los valores enum y seed

var example = new ExampleClass(); example.Faculty = FacultyEnum.Eng; if (example.Faculty == FacultyEnum.Math) { //code }

Agregue la semilla en YourDbContext.cs

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

Agregue la tabla de enumeración en su DbContext

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

Úsalo

var example = new ExampleClass(); example.Faculty = FacultyEnum.Eng; if (example.Faculty == FacultyEnum.Math) { //code }

Recordar

Si no agrega virtual en la propiedad de la Facultad, debe usar el método Incluir de DbSet para hacer Eager Load

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

Si la propiedad de la facultad es virtual, simplemente úsela

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

He seguido MSDN sobre cómo manejar enumeraciones en Code First para EF6. Funcionó, como se supone, pero el campo en la tabla creada que se refiere al enumerador es un int simple.

Preferiría que se produjera una segunda tabla, cuyos valores seguirían la definición del enumerador en el código C #. Entonces, en lugar de solo obtener una tabla correspondiente al Departamento en el ejemplo en MSDN, también me gustaría ver una segunda tabla poblada por los elementos de la Facultad .

public enum Faculty { Eng, Math, Eco } public partial class Department { [Key] public Guid ID { get; set; } [Required] public Faculty Name { get; set; } }

Al investigar el problema, me topé con una solution , que sugiere crear una tabla para la enumeración y rellenarla explícitamente al sembrar.

Me parece un enfoque engorroso y una gran cantidad de trabajo que debe manejarse de forma automática. Después de todo, el sistema sabe qué valores reales constituyen la enumeración. Desde el punto de vista de la base de datos, siguen siendo filas de datos, al igual que las entidades que creo pero desde el aspecto OO, no es realmente un dato, sino un tipo (expresado libremente) que puede asumir un número finito y previamente conocido de estados.

¿Se recomienda el enfoque de llenar la tabla "manualmente"?


Basado en la respuesta de @Alberto Monteiro, he creado una clase genérica en caso de que tenga varias tablas. El aviso aquí es que Id es el tipo de TEnum. Usarlo de esta manera proporcionará la opción de usar Enum para declarar el tipo de propiedad.

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

Por defecto, Enum usa números enteros, por lo que el proveedor de db creará un campo con el 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)}"); } } }

Ahora solo puedes heredar la 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! }

Sembrar los valores

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


Como EF no lo maneja automáticamente, , esta es la forma recomendada.

Sugiero algunas modificaciones en el artículo que proporcionó.

Renombra tu enumeración

public enum FacultyEnum { Eng, Math, Eco }

Crea una clase que represente la tabla

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

Su modelo hace referencia a la clase

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

Cree un método de extensión para obtener una descripción de los valores enum y 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; 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)); }

Agregue la semilla en Configuration.cs

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

Agregue la tabla de enumeración en su DbContext

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

Úsalo

var example = new ExampleClass(); example.Faculty = FacultyEnum.Eng; if (example.Faculty == FacultyEnum.Math) { //code }

Recordar

Si no agrega virtual en la propiedad de la Facultad, debe usar el método Incluir de DbSet para hacer Eager Load

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

Si la propiedad de la facultad es virtual, simplemente úsela

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


Debe agregar : byte delante de la declaración de enum :

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

En la base de datos, ¡debería ver TINYINT y no es necesario emitir!


Excelente @AlbertoMonterio! Para que esto funcione con ASP.NET CORE / EF Core, realicé algunos ajustes en la solución de Alberto.

Por brevedad, solo las modificaciones se muestran a continuación:

Cree un método de extensión para obtener una descripción de los valores enum y 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)); }

Agregue la semilla en Configuration.cs

Agregar siembra a OnModelCreating de DataContext

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


Otra posibilidad, si desea mantener su modelo más simple, estilo POCO, use la enumeración como una propiedad que será almacenada como un entero por el marco de la entidad.

Luego, si desea que las "tablas de enumeración" se creen y actualicen en su base de datos, le recomiendo usar el paquete nuget https://github.com/timabell/ef-enum-to-lookup y usarlo en una semilla de EF Migration método por ejemplo:

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

Esto creará la tabla "Forma" con 2 filas llamadas Cuadrado y Redondo, con la restricción de clave externa relevante en la tabla "Foo"


Otro enfoque que funciona (y me parece más simple) en EF Core:

Su enumeración

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

Tablas Db

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

Tu DbContext

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

En el código, básicamente solo uso el enum Color (nunca ColorDto). Pero todavía es bueno tener la tabla ''Colores'' con un FK en la tabla ''Objetos personalizados'' para consultas y vistas sql.