Полиморфные Ассоциации в Entity Framework

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

Пример:

Documents стол имеетDocumentID (первичный ключ идентификации), некоторые другие столбцы и 2 специальных столбца, называемыхObjectType а такжеObjectID.ЕслиObjectType='STUDENT', ObjectID указывает наStudents Таблица.ЕслиObjectType='TEACHER', ObjectID указывает наTeachers стол и т. д.

Это похоже наэтот дизайн (как описано в «подходе без внешнего ключа») илиэтот (описывается как анти-шаблон). И, очевидно, естьнет ограничений по внешнему ключу на этих столбцах(AFAIK никакая база данных не позволила бы такие отношения).

Я пишу новый уровень доступа к данным, используя Entity Framework 6 (сначала код с Fluent API), который должен работать бок о бок с существующим кодом. Так как эта структура базы данных была развернута для сотен разных клиентов (у каждого из которых была своя кодовая база и разные настройки базы данных), изменение структуры существующих таблиц не вариант.

Мой вопрос: как мне отобразить эти полиморфные ассоциации в мою модель кода EF?

РЕДАКТИРОВАТЬКажется, я пытался спроектировать иерархию классов на неправильных объектах. Насколько я понимаю, у меня было много «GenericChildTables» (таких как Documents), которые должны указывать на (несуществующий) объект, который имел бы составной ключ ObjectType + ObjectID. А затем я пытался создать эту новую сущность (назовем ее «BusinessObject») и отобразить мои основные сущности (учащиеся, учителя и т. Д.) Как подтипы этого BusinessObject.

ЭТО дизайн, вероятно, был просто неправильным и, возможно, совершенно невозможным, потому что создаваемая мной новая таблица (BusinessObject) зависела от StudentID / TeacherID и т. Д., Поэтому она не могла быть родительской для этих таблиц. Используя некоторые уродливые обходные пути, я мог создать этот BusinessObject как отдельный дочерний элемент для каждой основной сущности и сопоставить эти BusinessObjects с полиморфными таблицами, и он действительно работал, но в совершенно неправильном дизайне.

затем Я виделВопрос Герта Ардольда и понял, что то, что должно быть спроектировано как иерархия классов, это НЕ Студенты / Учителя / и т. д. (сгруппированные в общую сущность), а каждый из этих ChildTables, которые содержали различные подтипы в соответствии сObjectType дискриминатор - это те типы, которые следует разбить на подтипы.Смотрите мое решение на мой собственный ответ ниже.

 Gert Arnold08 июл. 2016 г., 23:52
ВотВот как я это сделал, используя базу данных сначала, но также описал, как я не смог перенести это на код сначала.
 drizin09 июл. 2016 г., 19:06
@GertArnold Вы пробовали что-то подобноеgist.github.com/mauriciolobo/40a4fc6017539c79135bf62f57ee80f1 ?
 Gert Arnold09 июл. 2016 г., 23:24
Нет, это не та модель (два внешних ключа).
 drizin10 июл. 2016 г., 01:21
@GertArnold Это был ответ друга на мой вопрос, но я не уверен, что он работал. Ваш дизайн указал мне верное направление, и я только что опубликовал ответ, и, похоже, он работает сейчас. Если я правильно понял ваш код, я думаю, что проблема заключалась в том, что OwnerId должен быть удален из базового класса (Comment) и быть частью только производных классов.

Ответы на вопрос(1)

Решение Вопроса

я пытался спроектировать иерархию классов на неправильных объектах. Насколько я понимаю, у меня было много «GenericChildTables» (таких как Documents), которые должны указывать на (несуществующий) объект, который имел бы составной ключ ObjectType + ObjectID. А затем я пытался создать эту новую сущность (назовем ее «BusinessObject») и отобразить мои основные сущности (учащиеся, учителя и т. Д.) Как подтипы этого BusinessObject.

Потом я увиделВопрос Герта Ардольда и понял, что правильный дизайн наследования был НЕ в группировке Учеников / Учителей / и т. д. в супертип, а в разделении этих GenericChildTables на несколько подтипов.

Я буду использовать таблицу «Документы» в качестве примера, чтобы показать, как я преобразовал эти GenericChildTables втонн в часи как я сопоставил свои основные сущности (студенты, учителя и т. д.) с коллекциями этих подтипов.

Сначала я создал производные классы (подтипы), добавил свойства навигации и сопоставил эти подтипы с базовым типом, используяObjectType как дискриминатор типа:

public class StudentDocument : Document
{
    public Student Student { get; set; }
    public int StudentID { get; set; } 
}
public class TeacherDocument : Document
{
    public Teacher Teacher { get; set; }
    public int TeacherID { get; set; } 
}
modelBuilder.Entity<Document>()
.Map<StudentDocument>(m => {
    m.Requires("ObjectType").HasValue("STUDENT");
})
.Map<TeacherDocument>(m => {
    m.Requires("ObjectType").HasValue("TEACHER");
});

Затем я добавил свойства навигации в свои основные классы (ученики и учителя), указывая на созданные подтипы:

partial class Student
{
   public virtual ICollection<StudentDocument> Documents { get; set; }
}
partial class Teacher
{
   public virtual ICollection<TeacherDocument> Documents { get; set; }
}

Я создал сопоставления для отношений Student.Documents и Teacher.Documents. Обратите внимание, что я использую свойства StudentID и TeacherID, но они физически сопоставлены со столбцом ObjectID:

var sl = modelBuilder.Entity<StudentDocument>();
sl.Property(t => t.StudentID).HasColumnName("ObjectID");
sl.HasRequired(t => t.Student).WithMany(t => t.Documents).HasForeignKey(d => d.StudentID);

var al = modelBuilder.Entity<TeacherDocument>();
al.Property(t => t.TeacherID).HasColumnName("ObjectID");
al.HasRequired(t => t.Teacher).WithMany(t => t.Documents).HasForeignKey(d => d.TeacherID);

Наконец-то яудален из базового типа (Документ) собственностьObjectType, потому что это дискриминатор типа, и должен использоваться только внутри (не может быть выставлен на класс).
Я такжеудален от базового типаObjectID потому что это должно быть отображено только на подтипы (сопоставлены соответственно как StudentID и TeacherID).

И все работало как шарм!

PS: обратите внимание, что если вы используете шаблоны T4 (сначала код из базы данных), они всегда будут восстанавливать эти свойства, потому что шаблоны ничего не знают о hiearchies, поэтому они отображают документы в одну сущность со свойствами для каждого столбца, поэтому вы должны исключить вручную эти свойства.

 Mardoxx12 нояб. 2016 г., 18:43
Ах да, да! Вы так говорите в первом предложении :)
 Mardoxx12 нояб. 2016 г., 18:33
У меня была игра с этим, и это, казалось, создало TPC или TPH, но без назначений FK. Какова была полученная схема?
 drizin12 нояб. 2016 г., 18:37
Mardoxx, на самом деле я использовал устаревшую базу данных, поэтому сначала я использовал код из базы данных (другими словами, я не создавал базу данных автоматически). Я действительно не могу комментировать генерацию схемы, потому что сначала я мало знаю о коде.

Ваш ответ на вопрос