web-dev-qa-db-fra.com

Comment créer une table correspondant à enum dans EF6 Code First?

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?

46
Konrad Viltersten

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.

Renomme ton enum

public enum FacultyEnum { Eng, Math, Eco }

Créer une classe qui représente la table

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

Votre modèle référence la classe

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

Créer une méthode d'extension pour obtenir une description à partir des valeurs enum et 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));
}

Ajouter la graine dans Configuration.cs

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

Ajouter la table enum dans votre DbContext

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

Utilise le

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

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

Se souvenir

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
}
86
Alberto Monteiro

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));
10
unsafePtr

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"

5
Michael

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!

0
محمد رضا

Alberto Monteiro a très bien répondu à cette question. J'ai dû faire quelques ajustements pour que cela fonctionne avec EF core. 

Renommez votre enum et ajoutez des décorateurs de description

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

Créer une classe qui représente la table

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

Votre modèle référence la classe

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

Créer une méthode d'extension pour obtenir une description à partir des valeurs enum et 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;
}

Ajouter la graine dans YourDbContext.cs

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

Ajouter la table enum dans votre DbContext

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

Utilise le

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

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

Se souvenir

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
}
0
Danwize