根据我对几个帖子的理解,使用共享主键时,使用EF的TPT架构不会创建必要的ON DELETE CASCADE ....还有人说,EF上下文将处理删除子的正确顺序-classed表(但我得到一个错误,它打破了约束,我可以通过在子类表上添加ON DELETE CASCADE来修复它)...
更多背景信息......
我有一个Section类,它有一个数字,标题和一个页面列表.该页面使用一个包含基本页面属性的超类来设计.我有大约10个以上的页面类子类.Section类包含这些页面的ICollection.除了在子类表上没有ON DELETE CASCADE之外,DB正确创建.
我的代码将创建实体并添加到数据库中.但是,如果我尝试删除一个部分(或所有部分),由于我的子类页面表上的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; }
}
Run Code Online (Sandbox Code Playgroud)
...加上大约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; }
...
}
Run Code Online (Sandbox Code Playgroud)
.. Fluent API ...(注意Schema定义为""用于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;
}
Run Code Online (Sandbox Code Playgroud)
...为其他表建模的其他代码......
因此,应用程序可以填充内容并正确设置关系.但是,当我尝试删除课程时,由于章节到PageContent表的约束,我得到删除失败的错误.
这是删除课程的代码(实际上我删除了所有课程)......
using (MyContext ctx = new MyContext())
{
ctx.Courses.ToList().ForEach(crs => ctx.Courses.Remove(crs));
AttachLookupEntities(ctx);
ctx.SaveChanges();
}
Run Code Online (Sandbox Code Playgroud)
如果我在ChapterPage和SectionPage表中为其共享的主要内容添加了"ON DELETE CASCADE",那么删除就会通过.
综上所述,
我见过的唯一解决方案是手动更改约束,为我的所有子类页表添加ON DELETE CASCADE.我可以实现更改,因为我有代码为我需要的EF表(我们整个数据库的一小部分)生成数据库脚本,因为我们不会使用EF来创建或实例化数据库(因为它不能正确支持迁移)到目前为止......)
我真诚地希望我错误地编写了一些内容,或者忘记了模型构建器逻辑中的一些设置.因为如果没有,EF设计师已经定义了一种架构(TPT设计方法),如果没有黑客攻击,它就无法在任何现实世界中使用.这是一个半成品解决方案.不要误会我的意思,我喜欢已完成的工作,并且像大多数MSFT解决方案一样,它可以满足70%的大多数基本应用程序的使用.它还没有为更复杂的情况做好准备.
我试图将数据库设计保持在EF流畅的API中并且是独立的.这对我来说大概是98%,如果他们完成了这项工作,也许在下一个版本中会很好.至少它节省了我所有的CRUD操作.
再见!吉姆肖
我用一个更简单的例子重现了这个问题:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Data.Entity;
namespace EFTPT
{
public class Parent
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<BasePage> Pages { get; set; }
}
public abstract class BasePage
{
public int Id { get; set; }
public string Name { get; set; }
public Parent Parent { get; set; }
}
public class DerivedPage : BasePage
{
public string DerivedName { get; set; }
}
public class MyContext : DbContext
{
public DbSet<Parent> Parents { get; set; }
public DbSet<BasePage> BasePages { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Parent>()
.HasMany(p => p.Pages)
.WithRequired(p => p.Parent); // creates casc. delete in DB
modelBuilder.Entity<BasePage>()
.ToTable("BasePages");
modelBuilder.Entity<DerivedPage>()
.ToTable("DerivedPages");
}
}
class Program
{
static void Main(string[] args)
{
using (var ctx = new MyContext())
{
var parent = new Parent { Pages = new List<BasePage>() };
var derivedPage = new DerivedPage();
parent.Pages.Add(derivedPage);
ctx.Parents.Add(parent);
ctx.SaveChanges();
}
using (var ctx = new MyContext())
{
var parent = ctx.Parents.FirstOrDefault();
ctx.Parents.Remove(parent);
ctx.SaveChanges(); // exception here
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
这也给你带来了同样的例外.只有解决方案似乎是:
Seed方法中放入了适当的SQL命令),手动为DB中的TPT约束设置级联删除.或者将TPT继承中涉及的entites加载到内存中.在我的示例代码中:
var parent = ctx.Parents.Include(p => p.Pages).FirstOrDefault();
Run Code Online (Sandbox Code Playgroud)
当实体加载到上下文中时,EF实际上创建了两个DELETE语句 - 一个用于基表,另一个用于派生表.在您的情况下,这是一个糟糕的解决方案,因为您必须先加载更复杂的对象图,然后才能获得TPT实体.
更有问题的是,如果Parent有ICollection<DerivedPage>(并且反向Parent属性在DerivedPage那里):
public class Parent
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<DerivedPage> Pages { get; set; }
}
public abstract class BasePage
{
public int Id { get; set; }
public string Name { get; set; }
}
public class DerivedPage : BasePage
{
public string DerivedName { get; set; }
public Parent Parent { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
示例代码不会抛出异常,而是从派生表中删除行而不是从基表中删除行,留下一个不能再代表实体的幻像行,因为它BasePage是抽象的.通过级联删除无法解决此问题,但实际上您必须先将该集合加载到上下文中,然后才能删除父级以避免数据库中出现这种无意义.
类似的问题和分析在这里:http://social.msdn.microsoft.com/Forums/en-US/adodotnetentityframework/thread/3c27d761-4d0a-4704-85f3-8566fa37d14e/
| 归档时间: |
|
| 查看次数: |
1959 次 |
| 最近记录: |