Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create a table corresponding to enum in EF6 Code First?

I've followed MSDN on how to handle enumerations in Code First for EF6. It worked, as supposed to but the field in the created table that refers to the enumerator is a simple int.

I'd prefer a second table to be produced, the values of which would follow the definition of the enumerator in C# code. So, instead of only getting a table corresponding to Department in the example on MSDN, I'd also like to see a second table populated by the items from Faculty.

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

Researching the issue, I stumbled upon a solution, which suggests creating a table for the enumeration and populating it explicitly by seeding.

It appear to me as a cumbersome approach and a lot of work that should be handled automagically. After all, the system knows what actual values that constitute the enumeration. From DB point of view it's still data rows, just as the entities that I create but from OO aspect, it's not really a data - rather a type (loosely expressed) that can assume a finite and onbeforehand known number of states.

Is the approach of populating the table "manually" recommended?

like image 690
Konrad Viltersten Avatar asked Jan 01 '16 15:01

Konrad Viltersten


People also ask

How do I list enums?

Get a list of Enum members. The idea is to use the Enum. GetValues() method to get an array of the enum constants' values. To get an IEnumerable<T> of all the values in the enum, call Cast<T>() on the array.

What is enum in Entity Framework?

In Entity Framework, this feature will allow you to define a property on a domain class that is an enum type and map it to a database column of an integer type. Entity Framework will then convert the database value to and from the relevant enum as it queries and saves data.

How do I assign an enum to a string?

There are two ways to convert an Enum to String in Java, first by using the name() method of Enum which is an implicit method and available to all Enum, and second by using toString() method.


2 Answers

Since EF doesn't handle it automatically, yes, this is the recommend way.

I suggest some modifications in article that you provided.

Rename your enum

public enum FacultyEnum { Eng, Math, Eco } 

Create a class that represent the 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; } 

Your model reference the class

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

Create a extension method to get description from enum and seed values

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

Add the seed in Configuration.cs

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

Add the enum table in your DbContext

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

Use it

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

To remember

If you don't add virtual in Faculty property, you must use Include method from DbSet to do Eager Load

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

If Faculty property is virtual, then just use it

var exampleFromDb = dbContext.Examples.Find(1); if (example.Faculty == FacultyEnum.Math) {     //code } 
like image 192
Alberto Monteiro Avatar answered Oct 05 '22 14:10

Alberto Monteiro


Based on @Alberto Monteiro answer i've created generic class in case when you have several tables. The notice here is that Id is the type of TEnum. Using it in such way will provide option to use Enum for declaring property type.

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

By default Enum using integers, so the db provider will create field with "int" type.

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

Now you just can inherit the 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! } 

Seed the values

context.QuestionTypes.SeedEnumValues<QuestionType, QuestionTypeEnum>(e => new QuestionType(e)); 
like image 33
unsafePtr Avatar answered Oct 05 '22 14:10

unsafePtr