2016-11-16 2 views
1

Резюме: в Entity Framework Я использую TPC для создания двух классов, полученных из того же базового класса. В беглом API я наследую свойства, но как моделировать свойства базового класса?Entity Framework: свойства суперкласса TPC MapInheritedProperties

Более подробное описание В Entity Framework У меня есть класс Child и два вида детей: мальчик и девочка. И мальчик и девочка проистекают из ребенка:

public class Child 
{ 
    public int Id {get; set;} 
    public string Name {get; set;} 
} 
public class Boy : Child 
{ 
    public string SomeBoyishProperty {get; set;} 
} 
public class Girl : Child 
{ 
    public string SomeGirlyProperty {get; set;} 
} 

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

public class MyDbContext : DbContext 
{ 
    public DbSet<Boy> Boys {get; set;} 
    public DbSet<Girl> Girls {get; set; 
} 

From several sources, for example this one я узнал, что это называется TPC: таблица на класс бетона и что я должен MapInheritedProperties в OnModelCreating

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

    // model the properties of the base class, for instance set max length 
    modelBuilder.Entity<Child>() 
     .Property(p => p.Name).IsRequired().HasMaxLength(12); 

    // Model Daughter: 
    modelBuilder.Entity<Daughter>() 
    .Map(m => 
    { 
     m.MapInheritedProperties(); 
     m.ToTable("Daughters"); 
    }) 
    .Property(p => p.SomeGirlyProperty).IsOptional().HasMaxLength(13); 

    // model Boy 
    modelBuilder.Entity<Son>() 
    .Map(m => 
    { 
     m.MapInheritedProperties(); 
     m.ToTable("Sons"); 
    }) 
    .Property(p => p.SomeBoyishProperty).IsOptional().HasMaxLength(14); 
} 

Во время SaveChanges я получаю InvlidOperationException о том, что первичный ключ не является уникальным. Удаление части, которая создает Child, решает эту проблему.

Как создать свойства ребенка без необходимости делать это в Girl и снова в свойствах Boy?

+0

ваша 'Ребенок 'класс абстрактный в вашей реальной модели? –

+0

Ребенок аннотация: Я не планировал создавать детские объекты, только мальчики и девочки. Я также не хочу создать таблицу Child. Поэтому, если бы я помог сделать Ребенок абстрактным, тогда я бы сделал это. Я мог бы, конечно, сделать мальчика композицией Ребенка, что бы решить проблему. Но мне кажется немного странным, что у мальчика есть ребенок, а не у ребенка. –

ответ

2

SHORT ОТВЕТ:

Если вы хотите, чтобы ваш код работать, удалите ссылку на Child объекта в конфигурации модели. Как только EF знает о Child как Entity, он будет применять следующее правило: не может быть 2 объекта типа Child или 2 объекта, которые наследуют от Child с тем же PK в памяти. Вы можете увидеть, что ошибка говорит вам, что объекты, которые успешно сохранялись; но когда EF вытаскивает новые идентификаторы, они обнаруживают, что оба имеют одинаковый идентификатор.

LONG ОТВЕТ

Удалить

modelBuilder.Entity<Child>() 
    .Property(p => p.Name).IsRequired().HasMaxLength(12); 

Вместо этого, как ваш метод OnModelCreating должен выглядеть.

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

    // Model Daughter: 
    var girlEntity = modelBuilder.Entity<Girl>(); 
    girlEntity.Map(m => 
    { 
     m.MapInheritedProperties(); 
     m.ToTable("Daughters"); 
    }); 
    girlEntity.Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); 
    girlEntity.Property(p => p.Name).IsRequired().HasMaxLength(12); 
    girlEntity.Property(p => p.SomeGirlyProperty).IsOptional().HasMaxLength(13); 

    // model Boy 
    var boyEntity = modelBuilder.Entity<Boy>(); 
    boyEntity.Map(m => 
    { 
     m.MapInheritedProperties(); 
     m.ToTable("Sons"); 
    }); 
    boyEntity.Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); 
    boyEntity.Property(p => p.Name).IsRequired().HasMaxLength(12); 
    boyEntity.Property(p => p.SomeBoyishProperty).IsOptional().HasMaxLength(14); 
} 

Если вы не хотите повторения конфигурации в конфигурации я использовал бы DataAnnotations атрибуты на базовый класс для обеспечения имя, которое будет необходимо.

Вам также необходимо обеспечить, чтобы свойство Id было автоматически сгенерировано в базе данных. Этого не происходит по соглашению при использовании метода Map в свободном API. Вы можете видеть, что я добавил бесплатные звонки, чтобы это произошло как в картинах Girl, так и в Boy.

Надеюсь, это поможет.

2

Я переработал предложение Артуро. Это решение было слишком длинным, чтобы описать как комментарий. Итак, Артуро: спасибо, что дал мне идеи. Chapeau!

Артуро предложил использовать аннотации данных. Причина, по которой я не хочу использовать этот метод, заключается в том, что моделирование класса не обязательно соответствует определенному представлению базы данных. Я немного гипотетически, но если я хочу меньшую максимальную длину для имени мальчика, чем для имени девушки, тогда аннотации данных не помогут.

Кроме того, есть некоторые вещи, которые необходимо выполнить с использованием свободного API. Например, вы не можете сказать, что System.DateTime имеет формат DateTime2 в базе данных с использованием DataAnnotations.

Если вы этого не догадались: мое описание проблемы было очень упрощено. Все три класса имеют много свойств, которые необходимо много беглых конфигураций API

замечания

Артуро помогли мне следующее решение:

internal class ChildConfig<T> : EntityTypeConfiguration<T> where T : Child 
{ 
    public ChildConfig(...) 
    { 
     // configure all Child properties 
     this.Property(p => p.Name).... 
    } 
} 
internal class BoyConfig : ChildConfig<Boy> 
{ 
    public BoyConfig(...) : base (...) 
    { 
     // the base class will configure the Child properties 
     // configure the Boy properties here 
     this.Property(p => p.SomeBoyishProperty)... 
    } 
} 

И в MyDbContext:

protected override void OnModelCreating(DbModelBuilder modelBuilder) 
{ 
    modelBuilder.Configurations.Add(new BoyConfig(...)); 
    modelBuilder.Configuration.Add(new GirlConfig(...)); 
} 
+0

Превосходное использование классов конфигурации. –