Использование перечислимых классов в 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();
}