J'ai suivi MSDN pour savoir comment gérer les énumérations dans Code First pour EF6. Cela a fonctionné, comme supposé mais le champ de la table créée qui fait référence à l'énumérateur est un simple int.
Je préférerais qu'un deuxième tableau soit produit, dont les valeurs suivraient la définition de l'énumérateur dans le code C #. Ainsi, au lieu de n'obtenir qu'un tableau correspondant à Department dans l'exemple de MSDN, j'aimerais également qu'un second tableau soit rempli avec les éléments de Faculty.
public enum Faculty { Eng, Math, Eco }
public partial class Department
{
[Key] public Guid ID { get; set; }
[Required] public Faculty Name { get; set; }
}
En recherchant la question, je suis tombé sur une solution , qui suggère de créer un tableau pour le dénombrement et de le remplir explicitement par ensemencement.
Cela me semble être une approche lourde et beaucoup de travail qui devrait être traité automatiquement. Après tout, le système sait quelles valeurs réelles constituent l'énumération. Du point de vue de la base de données, il s’agit toujours de lignes de données, tout comme les entités que je crée mais sous l’aspect OO, il ne s’agit pas vraiment d’une donnée - mais d’un type (exprimé de manière approximative) pouvant prendre un nombre d’états fini et préalablement connu. .
L'approche consistant à remplir la table "manuellement" est-elle recommandée?
Etant donné que EF ne le gère pas automatiquement, oui , c’est la méthode recommandée.
Je suggère quelques modifications dans l'article que vous avez fourni.
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<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));
}
protected override void Seed(Temp.MyClass context)
{
context.Facultys.SeedEnumValues<Faculty, FacultyEnum>(@enum => @enum);
context.SaveChanges();
}
public class MyClass : DbContext
{
public DbSet<ExampleClass> Examples { get; set; }
public DbSet<Faculty> Facultys { get; set; }
}
var example = new ExampleClass();
example.Faculty = FacultyEnum.Eng;
if (example.Faculty == FacultyEnum.Math)
{
//code
}
Si vous n’ajoutez pas de propriété virtuelle dans Faculty, vous devez utiliser la méthode Include de DbSet pour effectuer une charge utile
var exampleFromDb = dbContext.Examples.Include(x => x.Faculty).SingleOrDefault(e => e.Id == 1);
if (example.Faculty == FacultyEnum.Math)
{
//code
}
Si la propriété Faculty est virtuelle, utilisez-la simplement
var exampleFromDb = dbContext.Examples.Find(1);
if (example.Faculty == FacultyEnum.Math)
{
//code
}
Basé sur @Alberto Monteiro answer, j'ai créé une classe générique au cas où vous auriez plusieurs tables. L'avis ici est que Id est le type de TEnum. Son utilisation de cette manière fournira l'option d'utiliser Enum pour déclarer le type de propriété.
public class Question
{
public QuestionTypeEnum QuestionTypeId { get; set; } // field property
public QuestionType QuestionType { get; set; } // navigation property
}
Par défaut, Enum utilisant des entiers, le fournisseur de base de données crée un champ de type "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)}");
}
}
}
Maintenant, vous pouvez hériter de 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!
}
Semer les valeurs
context.QuestionTypes.SeedEnumValues<QuestionType, QuestionTypeEnum>(e => new QuestionType(e));
Une autre possibilité, si vous souhaitez conserver votre modèle plus simple en tant que style POCO, consiste à utiliser la propriété Enum qui sera stockée sous la forme d'un cadre entier par entité.
Ensuite, si vous souhaitez que les "tables enum" soient créées et mises à jour dans votre base de données, je vous recommande d'utiliser le package nuget https://github.com/timabell/ef-enum-to-lookup et de l'utiliser une méthode de départ EF Migration, par exemple:
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);
}
Cela créera la table "Shape" avec 2 lignes nommées Square et Round, avec la contrainte de clé étrangère correspondante dans la table "Foo"
Vous devriez ajouter : byte
devant la déclaration enum
:
enum MyFieldEnum : byte{
one = 1,
two = 2,
three = 4
}
Dans la base de données, vous devriez voir TINYINT
et pas besoin de lancer!
Alberto Monteiro a très bien répondu à cette question. J'ai dû faire quelques ajustements pour que cela fonctionne avec EF core.
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<TEnum>(this TEnum item)
=> item.GetType()
.GetField(item.ToString())
.GetCustomAttributes(typeof(DescriptionAttribute), false)
.Cast<DescriptionAttribute>()
.FirstOrDefault()?.Description ?? string.Empty;
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Faculty>().HasData(FacultyEnum.Eng, FacultyEnum.Math, FacultyEnum.Eco);
}
public class MyClass : DbContext
{
public DbSet<ExampleClass> Examples { get; set; }
public DbSet<Faculty> Facultys { get; set; }
}
var example = new ExampleClass();
example.Faculty = FacultyEnum.Eng;
if (example.Faculty == FacultyEnum.Math)
{
//code
}
Si vous n’ajoutez pas de propriété virtuelle dans Faculty, vous devez utiliser la méthode Include de DbSet pour effectuer une charge utile
var exampleFromDb = dbContext.Examples.Include(x => x.Faculty).SingleOrDefault(e => e.Id == 1);
if (example.Faculty == FacultyEnum.Math)
{
//code
}
Si la propriété Faculty est virtuelle, utilisez-la simplement
var exampleFromDb = dbContext.Examples.Find(1);
if (example.Faculty == FacultyEnum.Math)
{
//code
}