Hello It's me
Hello It's me

Reputation: 558

Using enumeration classes in EF

I'm using enumeration classes based on this article from Jimmy Bogard in my domain model. However, EF treats the classes as entities and wants a key to be defined.

Is there a way to make EF store the values from the enumeration classes like it does it with standard C# enums?

This is how I currently implemented the enumeration class:

public class ReservationStatus : Enumeration
{
    public static readonly ReservationStatus Outstanding = new ReservationStatus(0, "Oustanding");
    public static readonly ReservationStatus Paid = new ReservationStatus(1, "Paid");
    public static readonly ReservationStatus Canceled = new ReservationStatus(2, "Canceled");
    public static readonly ReservationStatus Rejected = new ReservationStatus(3, "Rejected");

    private ReservationStatus() { }
    private ReservationStatus(int value, string displayName) : base(value, displayName) { }
}

public class Reservation : Entity<int>
{
    public ReservationStatus Status { get; set; }
}

I'm using EF 7 RC1.

Upvotes: 4

Views: 3471

Answers (3)

Mahmut Jomaa
Mahmut Jomaa

Reputation: 21

For those using EF Core 3.1 there is a better way to do it. The goal is to have a database table with the enumerations listed.

(1) Define how the enumeration table should look like:

public virtual DbSet<ReservationStatus> ReservationStatuses { get; set; }
 
public void Configure(EntityTypeBuilder<ReservationStatus> builder)
{
    builder.HasKey(x => x.Id);
    builder.Property(x => x.Id).HasDefaultValue(1).ValueGeneratedNever().IsRequired();
    builder.Property(x => x.Name).IsRequired();
}

(2) Define how the value relates in the reservation entity

 public void Configure(EntityTypeBuilder<Reservation> builder)
 {
     builder.Property(x => x.ReservationStatusId).IsRequired();
     builder.HasOne(x => x.ReservationStatus)
         .WithMany()
         .HasForeignKey(x => x.ReservationStatusId);
 }

(3) Seed the enumeration table

if (!await RevervationSatuses.AnyAsync())
{
    RevervationSatuses.AddRange(Enumeration.GetAll<RevervationSatus>());
    await SaveChangesAsync();
}

Upvotes: 1

J&#228;mes
J&#228;mes

Reputation: 7245

For the ones who come across this well referenced thread using EF Core, I usually store custom type enumerations by its value (aka identifier) in the database using type conversions. This extension method can be used in the method OnModelCreating (or a class that implements IEntityTypeConfiguration<T>):

internal static class EnumerationConfiguration
{
    public static void OwnEnumeration<TEntity, TEnum>(this EntityTypeBuilder<TEntity> builder,
        Expression<Func<TEntity, TEnum>> property)
        where TEntity : class
        where TEnum : Enumeration
    {
        builder
            .Property(property)
            .HasConversion(x => x.Id, x => Enumeration.FromId<TEnum>(x));
    }
}

Afterwards, an enumeration property can be configured as follows:

internal class SpecimenConfiguration : IEntityTypeConfiguration<Specimen>
{
    public void Configure(EntityTypeBuilder<Specimen> builder)
    {
        builder.OwnEnumeration(x => x.Type);
    }
}

The benefits of this approach is that it only stores the identifier (and not the other properties attached to a given derived class of Enumeration) and supports subclassing of Enumeration.

The downside is an heavily use of reflection through Enumeration.FromId<TEnum>(x). This can be amortized by introducing caching in this method, which is not provided by the linked article of Jimmy Bogard.

Upvotes: 8

Ivan Stoev
Ivan Stoev

Reputation: 205599

In EF6 you could configure your ReservationClass as ComplexType, ignore the DisplayName property and map Value property.

Unfortunately for you, according to these design meeting notes:

In the initial RTM of EF7 we are not planning to enable complex and/or value types.

In other words, you cannot do that in current EF Core.

Upvotes: 1

Related Questions