Ho seguito MSDN su come gestire le enumerazioni in Code First per EF6. Ha funzionato, come previsto, ma il campo nella tabella creata che fa riferimento all’enumeratore è un semplice int .
Preferirei che fosse prodotta una seconda tabella, i cui valori avrebbero seguito la definizione dell’enumeratore nel codice C #. Quindi, invece di ottenere solo una tabella corrispondente a Department nell’esempio su MSDN, mi piacerebbe anche vedere una seconda tabella popolata dagli elementi di Faculty .
public enum Faculty { Eng, Math, Eco } public partial class Department { [Key] public Guid ID { get; set; } [Required] public Faculty Name { get; set; } }
Ricercando il problema, mi sono imbattuto in una soluzione , che suggerisce di creare una tabella per l’enumerazione e di popolarla esplicitamente tramite il seeding.
Mi sembra un approccio ingombrante e un sacco di lavoro che dovrebbe essere gestito automagicamente. Dopo tutto, il sistema sa quali valori effettivi costituiscono l’enumerazione. Dal punto di vista del DB, sono ancora file di dati, proprio come le quadro che creo ma dall’aspetto di OO, non è realmente un dato – piuttosto un tipo (liberamente express) che può assumere un numero di stati finiti e prima conosciuti.
È raccomandato l’approccio di popolamento della tabella “manualmente”?
Poiché EF non lo gestisce automaticamente, sì , questo è il metodo consigliato.
Suggerisco alcune modifiche all’articolo che hai fornito.
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
protected override void Seed(Temp.MyClass context) { context.Facultys.SeedEnumValues(@enum => @enum); context.SaveChanges(); }
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 }
Se non si aggiunge virtual nella proprietà Faculty, è necessario utilizzare il metodo Include da DbSet per eseguire Eager Load
var exampleFromDb = dbContext.Examples.Include(x => x.Faculty).SingleOrDefault(e => e.Id == 1); if (example.Faculty == FacultyEnum.Math) { //code }
Se la proprietà Faculty è virtuale, basta usarla
var exampleFromDb = dbContext.Examples.Find(1); if (example.Faculty == FacultyEnum.Math) { //code }
In base alla risposta @ Alberto Monteiro, ho creato una class generica nel caso in cui si disponga di più tabelle. L’avviso qui è che Id è il tipo di TEnum. Usarlo in questo modo fornirà l’opzione per usare Enum per dichiarare il tipo di proprietà.
public class Question { public QuestionTypeEnum QuestionTypeId { get; set; } // field property public QuestionType QuestionType { get; set; } // navigation property }
Per impostazione predefinita, Enum utilizza numeri interi, quindi il provider db creerà un campo con il tipo “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)}"); } } }
Ora puoi ereditare l’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! }
Seme i valori
context.QuestionTypes.SeedEnumValues(e => new QuestionType(e));
Un’altra possibilità, se vuoi mantenere il tuo modello più semplice come stile POCO, è usare l’enum con una proprietà che verrà memorizzata come un intero dal framework di entity framework.
Quindi, se si desidera che le “tabelle enum” vengano create e aggiornate nel DB, si consiglia di utilizzare il pacchetto nuget https://github.com/timabell/ef-enum-to-lookup e utilizzarlo in un seme di migrazione EF metodo per esempio:
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); }
Questo creerà la tabella “Shape” con 2 righe denominate Square e Round, con il relativo vincolo di chiave esterna nella tabella “Foo”
È necessario aggiungere : byte
davanti alla dichiarazione enum
:
enum MyFieldEnum : byte{ one = 1, two = 2, three = 4 }
Nel database, dovresti vedere TINYINT
e non c’è bisogno di lanciare!
Alberto Monteiro ha risposto molto bene. Ho dovuto apportare alcune modifiche per farlo funzionare con core EF.
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(this TEnum item) => item.GetType() .GetField(item.ToString()) .GetCustomAttributes(typeof(DescriptionAttribute), false) .Cast() .FirstOrDefault()?.Description ?? string.Empty; }
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity().HasData(FacultyEnum.Eng, FacultyEnum.Math, FacultyEnum.Eco); }
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 }
Se non si aggiunge virtual nella proprietà Faculty, è necessario utilizzare il metodo Include da DbSet per eseguire Eager Load
var exampleFromDb = dbContext.Examples.Include(x => x.Faculty).SingleOrDefault(e => e.Id == 1); if (example.Faculty == FacultyEnum.Math) { //code }
Se la proprietà Faculty è virtuale, basta usarla
var exampleFromDb = dbContext.Examples.Find(1); if (example.Faculty == FacultyEnum.Math) { //code }