#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, который позволит вам расширять ваши типы по вашему желанию.