ADO EF код первого общего сопоставления наследования промежуточного класса

#entity-framework #generics #inheritance #mapping #entity-framework-4.1

#entity-framework #обобщения #наследование #отображение #entity-framework-4.1

Вопрос:

У меня есть следующее требование, которое хорошо работает в пространстве OO, но, похоже, я не могу заставить его отобразить обратно в БД, сначала используя ADO EF code.

У меня есть множество продуктов, каждый из которых будет иметь разные аспекты (атрибуты, но не в смысле атрибутов кода). Например, кольцо будет иметь такие аспекты, как тип минерала = золото и т.д., В то время как бриллиант будет иметь спецификацию clarity = VVSI1.

Как вы можете видеть, продукты очень сильно влияют на их состав, и я хочу динамичный способ расширения моей системы.

Таким образом, я создал класс продукта:

 public class Product
{
    public int id { get; set; }
    public string Name { get; set; }
    private List<ProductAspect> aspects = new List<ProductAspect>();
    public List<ProductAspect> Aspects { get { return aspects; } set { aspects = value; } }
}
  

В нем есть список ProductAspect, который является базовым классом для всех аспектов, продвигающихся вперед:

 public class ProductAspect 
{
    public int id { get; set; }
    public string AspectName { get; set; }
}
  

Затем я наследую от ProductAspect, используя общий, который позволяет мне быть конкретным (строго типизированным) в отношении значения моего аспекта:

 public abstract class ProductAspect<T> : ProductAspect
{
    public T AspectValue { get; set; }
}
  

Затем я создаю некоторые аспекты, которые позволят мне украсить мой продукт:

 public class StringAspect : ProductAspect<string> { };
public class DecimalAspect : ProductAspect<decimal> { };
public class ImageAspect : ProductAspect<byte[]> { };
  

Затем я попробовал DbContext и попробовал оба сопоставления наследования TPH и TPC.

Похоже, ни то, ни другое не работает. Модель БД, сгенерированная get, не создает ключ foriegn для таблиц StringAspect или DecimalAspect из таблицы Aspect.

 public class IxamDataContext : DbContext
{
    public DbSet<Product> Products { get; set; }
    public DbSet<ProductAspect> Aspects { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        AspectMapping(modelBuilder);
    }

    private void AspectMapping(DbModelBuilder mb)
    {
        //TPH
        //mb.Entity<ProductAspect>()
        //    .Map<StringAspect>(m => m.Requires("type").HasValue("sa"))
        //    .Map<DecimalAspect>(m => m.Requires("type").HasValue("da"));

        //TPC
        //mb.Entity<StringAspect>().ToTable("StringAspect");
        //mb.Entity<DecimalAspect>().ToTable("DecimalAspect");
    }
}
  

В результате возникает следующее исключение для этого исходного кода:

         Product p = new Product();
        p.Name = "Diamond";
        p.Aspects.Add(new StringAspect() { AspectName = "History", AspectValue = "Old and long" });
        p.Aspects.Add(new DecimalAspect() { AspectName = "Weight", AspectValue= 96.5M });


        context.Products.Add(p);
        context.SaveChanges();
  

Исключение:

Тип объекта ‘StringAspect’ не существует в наборе объектов ‘IxamDataContext.Aspects’. Имя параметра: сущность

Есть какие-нибудь идеи от профессионалов EF code first?

Ответ №1:

Entity Framework не поддерживает промежуточные, не сопоставленные типы в иерархии наследования. Это означает, что у вас не может быть такого наследования: A (отображено) -> B (не отображено) -> C (отображено). EF также не поддерживает отображение общих типов. Это означает, что вы должны удалить свой общий промежуточный класс из иерархии и перейти AspectValue к производным типам с правильным типом.

Комментарии:

1. Случайно, вы не знаете, поддерживает ли это EF Core 2.0? (может быть, самое подходящее время для миграции …)

Ответ №2:

Может быть, это слишком поздно, но я бы предложил вам использовать атрибут complexType, который позволит вам расширять ваши типы по вашему желанию.