2013-08-30 3 views
0

Я реализовал простое наследование с использованием EF5 с кодовым первым TPT. Мой базовый класс - «Человек», а мой унаследованный класс - «Пользователь». Таблицы называются соответственно.EF5 Наследование двух уровней, TPT + TPH (сначала код)

В связи с тем, что мое программное обеспечение снова будет использоваться в качестве основы для других людей, чтобы создать его поверх, я хочу дать им простой инструмент для расширения программного обеспечения без изменения базы данных. Мне нужно только получить правильный тип класса, мне не нужно его обновлять. Для этого я хочу специализированный уровень для пользователей с TPH. Разработчики добавят свой класс в код и вставляют свою запись, маркирующую тип.

Я добавил в таблицу пользователей, поле «дискриминатор», но теперь я получаю эту ошибку, пытаясь загрузить модель:

Error 3032: Problem in mapping fragments : mapped to the same rows in table 

Это не совсем понятно, что означает, что ошибка .. Может кто-нибудь предложить объяснение/решение для этого?

Заранее спасибо

ответ

1

После некоторого копания, я обнаружил, что это возможно, на самом деле, я не уверен, так как какая версия, но она работает как шарм на EF5.

Решение вышеуказанной ошибки заключается в том, чтобы сопоставить отношение вручную с Fluent API. Слой ТПТА требует:

  • отношения наследования между двумя классами
  • Тем же первичным ключом на оба таблицах, и для таблицы унаследованного типа внешнего ключа первичного ключа.

Вот определение классов:

[Table("Persons", Schema="MySchema")] 
public partial class Person 
{ 
    public int PersonId { get; set; } 
    public string Name { get; set; } 
} 

[Table("Users", Schema = "MySchema")] 
partial class User : Person 
{ 
} 

TPH слой, кажется, не работает, просто добавив поле «дискриминатор». Найденное решение:

  • Добавление поля «UserType» в базу данных.
  • Составьте карту, используя Fluent API.

Важно отметить, что поле «UserType» не может быть включено в определение класса или вы получите ошибку выше.

public class CustomUser : User 
{ 
} 

Внутри класса DbContext на OnModelCreating переопределения:

modelBuilder.Entity<User>().Map<User>(m => 
    { 
     m.ToTable("Users"); 
     m.Requires("UserType").HasValue("User"); 
    }).Map<CustomUser>(m => 
    { 
     m.Requires("UserType").HasValue("CustomUser"); 
    }); 

Наконец, имея такой код в DbContext не очень многоразовые, поэтому я переехал его внутри класса EntityTypeConfiguration следующим образом :

public class CustomUserConfiguration : EntityTypeConfiguration<CustomUser> 
{ 
    public CustomUserConfiguration() 
    { 
     Map<CustomUser>(m => m.Requires("UserType").HasValue("CustomUser")); 
    } 
} 

DbContext теперь может использовать немного отражения, чтобы загрузить все классы EntityTypeConfiguration

protected override void OnModelCreating(DbModelBuilder modelBuilder) 
{ 
    MethodInfo addMethod = typeof(ConfigurationRegistrar).GetMethods().Single(m => m.Name == "Add" && m.GetGenericArguments().Any(a => a.Name == "TEntityType")); 
    IList<Assembly> assemblies = AppDomain.CurrentDomain.GetAssemblies().Where(a => !a.GetName().Name.StartsWith("System") && !a.GetName().Name.StartsWith("Microsoft")).ToList(); 
    foreach (Assembly assembly in assemblies) 
    { 
     IList<Type> types = assembly.GetTypes().Where(t => t.BaseType != null && t.BaseType.IsGenericType && t.BaseType.GetGenericTypeDefinition() == typeof(EntityTypeConfiguration<>)).ToList(); 
     foreach (Type type in types) 
     { 
      Type entityType = type.BaseType.GetGenericArguments().Single(); 
      object entityConfig = assembly.CreateInstance(type.FullName); 
      addMethod.MakeGenericMethod(entityType).Invoke(modelBuilder.Configurations, new object[] { entityConfig }); 
     } 
    } 
}