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, sí , 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.