Из того, что я понимаю в нескольких сообщениях, архитектура TPT с EF не создает необходимого ON DELETE CASCADE при использовании общего первичного ключа .... Также было сказано, что контекст EF будет обрабатывать правильный порядок удаления из подклассовых таблиц (однако я получаю сообщение об ошибке, которое нарушает ограничение и что я могу это исправить, добавив ON DELETE CASCADE в таблицу подклассов) ...
дополнительная справочная информация ...
У меня есть класс Section, в котором есть номер, заголовок и список страниц. Страница разработана с использованием суперкласса, который содержит основные свойства страницы. У меня есть около 10+ подклассов класса страницы. Класс Section содержит ICollection этих страниц. БД создана правильно, за исключением того, что в подклассовых таблицах нет ON DELETE CASCADE.
Мой код создаст сущности и добавит в БД штрафа. Однако, если я пытаюсь удалить раздел (или все разделы), он не удаляется из-за ограничения FK в моей таблице страниц подкласса ...
public abstract BaseContent
{
... common properties which are Ignored in the DB ...
}
public class Course : BaseContent
{
public int Id {get;set;}
public string Name {get;set;}
public string Descripiton {get;set;}
public virtual ICollection<Chapter> Chapters{get;set;}
...
}
public class Chapter : BaseContent
{
public int Id {get;set;}
public int Number {get;set;}
public string Title {get;set;}
public virtual Course MyCourse{get;set;}
public virtual ICollection<Section> Sections{get;set;}
...
}
public class Section : BaseContent
{
public int Id {get;set;}
public int Number {get;set;}
public string Title {get;set;}
public virtual Chapter MyChapter {get;set;}
public virtual ICollection<BasePage> Pages {get;set;}
...
}
public abstract class BasePage : BaseContent, IComparable
{
public int Id { get; set; }
public string Title { get; set; }
public string PageImageRef { get; set; }
public ePageImageLocation ImageLocationOnPage { get; set; }
public int PageNumber { get; set; }
public virtual Section MySection { get; set; }
...
}
public class ChapterPage : BasePage
{
public virtual int ChapterNumber { get; set; }
public virtual string ChapterTitle { get; set; }
public virtual string AudioRef { get; set; }
}
public class SectionPage : BasePage
{
public virtual int SectionNumber { get; set; }
public virtual string SectionTitle { get; set; }
public virtual string SectionIntroduction { get; set; }
}
... плюс около 8 других подклассов BasePage ...
public class MyContext: DbContext
{
...
public DbSet<Course> Courses { get; set; }
public DbSet<Chapter> Chapters { get; set; }
public DbSet<Section> Sections { get; set; }
public DbSet<BasePage> Pages { get; set; }
...
}
.. Свободный API ... (обратите внимание, что для SqlServer схема определяется как "", для Oracle - имя схемы)
private EntityTypeConfiguration<T> configureTablePerType<T>(string tableName) where T : BaseContent
{
var config = new EntityTypeConfiguration<T>();
config.ToTable(tableName, Schema);
// This adds the appropriate Ignore calls on config for the base class BaseContent
DataAccessUtilityClass.IgnoreAllBaseContentProperties<T>(config);
return config;
}
public virtual EntityTypeConfiguration<BasePage> ConfigurePageContent()
{
var config = configureTablePerType<BasePage>("PageContent");
config.HasKey(pg => pg.Id);
config.HasRequired(pg => pg.Title);
config.HasOptional(pg => pg.PageImageRef);
config.Ignore(pg => pg.ImageLocationOnPage);
return config;
}
public virtual EntityTypeConfiguration<ChapterPage> ConfigureChapterPage()
{
var config = configureTablePerType<ChapterPage>("ChapterPage");
config.HasOptional(pg => pg.AudioRef);
config.Ignore(pg => pg.ChapterNumber);
config.Ignore(pg => pg.ChapterTitle);
return config;
}
public virtual EntityTypeConfiguration<SectionPage> ConfigureSectionPage()
{
var config = configureTablePerType<SectionPage>("SectionPage");
config.HasOptional(pg => pg.AudioRef);
config.Ignore(pg => pg.SectionNumber);
config.Ignore(pg => pg.SectionTitle);
return config;
}
... другой код для моделирования других таблиц ...
Таким образом, приложение может заполнять контент и правильно устанавливать отношения. Однако, когда я пытаюсь удалить курс, я получаю сообщение об ошибке, что удаление не удалось из-за ограничения таблицы ChapterPage to PageContent ..
Вот код, который удаляет Курс (фактически я удаляю все курсы) ...
using (MyContext ctx = new MyContext())
{
ctx.Courses.ToList().ForEach(crs => ctx.Courses.Remove(crs));
AttachLookupEntities(ctx);
ctx.SaveChanges();
}
Если я добавлю 'ON DELETE CASCADE' в таблицу ChapterPage и SectionPage для общего ресурса с PageContent, удаление будет завершено.
В итоге
Единственное решение, которое я видел, - это вручную изменить ограничения, чтобы добавить ON DELETE CASCADE для всех моих таблиц страниц подкласса. Я могу реализовать это изменение, поскольку у меня есть код, который генерирует сценарий БД для нужных мне таблиц EF (небольшое подмножество всей нашей БД), поскольку мы не будем использовать EF для создания или создания экземпляра БД (так как он не поддерживает миграции должным образом пока что ...).
Я искренне надеюсь, что что-то неправильно кодировал или забыл некоторые настройки в логике построителя моделей. Потому что, если нет, разработчики EF определили архитектуру (подход к проектированию TPT), которая не может быть использована в любой реальной ситуации без обходного пути. Это наполовину законченное решение. Не поймите меня неправильно, мне нравится проделанная работа, и, как и большинство решений MSFT, она работает для 70% большинства основных приложений. Он просто не готов к более сложным ситуациям.
Я пытался сохранить дизайн БД полностью в рамках API EF, свободный и автономный. Для меня это примерно 98%, просто было бы неплохо, если бы они закончили работу, возможно, в следующем выпуске. По крайней мере, это спасает меня от всех операций CRUD.
Ciao!
Джим Шоу