Использование перечислимых классов в EF

Я использую перечисляющие классы, основанные на этой статье от Джимми Богарда, в моей доменной модели. Однако EF рассматривает классы как сущности и хочет, чтобы был определен ключ.

Есть ли способ заставить EF хранить значения из классов перечисления, как это делает со стандартными перечислениями C#?

Вот как я сейчас реализовал класс перечисления:

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

Я использую EF 7 RC1.

1 ответ

Решение

В EF6 вы можете настроить свой ReservationClass как ComplexType, игнорировать DisplayName недвижимость и карта Value имущество.

К сожалению для вас, в соответствии с этими примечаниями встречи дизайна:

В первоначальном RTM EF7 мы не планируем включать сложные и / или типы значений.

Другими словами, вы не можете сделать это в текущем EF Core.

Для тех, кто сталкивается с этим потоком с хорошими ссылками, использующим EF Core, я обычно сохраняю настраиваемые перечисления типов по его значению (так называемому идентификатору) в базе данных, используя преобразования типов. Этот метод расширения можно использовать в методеOnModelCreating (или класс, реализующий 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));
    }
}

После этого свойство перечисления можно настроить следующим образом:

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

Преимущества этого подхода заключаются в том, что он сохраняет только идентификатор (а не другие свойства, прикрепленные к данному производному классу Enumeration) и поддерживает подклассы Enumeration.

Обратной стороной является интенсивное использование отражения через Enumeration.FromId<TEnum>(x). Это можно окупить, добавив кэширование в этот метод, чего нет в связанной статье Джимми Богарда.

Для тех, кто использует EF Core 3.1, есть лучший способ сделать это. Цель состоит в том, чтобы иметь таблицу базы данных с перечисленными перечислениями.

(1) Определите, как должна выглядеть таблица перечисления:

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) Определите, как стоимость соотносится в объекте резервирования.

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

(3) Заполните таблицу перечисления

if (!await RevervationSatuses.AnyAsync())
{
    RevervationSatuses.AddRange(Enumeration.GetAll<RevervationSatus>());
    await SaveChangesAsync();
}
Другие вопросы по тегам