Entity Framework:为什么WillCascadeOnDelete()方法被忽略?

13

这是我的情况:

public abstract class Article
{
    [key]
    public Guid Guid { get; set;}

    public string Name { get; set;}
    .
    .
    .
}

public class Download : Article
{
    ...
}

public abstract class Category : Article
{
    ...
}

public class DownloadCategory : Category 
{
    ....
}

然后,我应该在下载和“DownloadCategory”之间建立多对多关系,如下所示:
public class DownloadInCategory
{
    [Key, Column(Order = 1), Required]
    [ForeignKey("Download")]
    Public Guid DownloadGuid { get; set; }

    Public Download Download { get; set; }

    [Key, Column(Order = 2), Required]
    [ForeignKey("Category")]
    Public Guid CategoryGuid { get; set; }

    Public DownloadCategory Category { get; set; }
}

当我调用Add-Migration命令时,为DownloadInCategory实体创建的迁移如下:
CreateTable("dbo.DownloadInCategories",
c => new
{
    CategoryGuid = c.Guid(nullable: false),
    DownloadGuid = c.Guid(nullable: false),
})
.PrimaryKey(t => new { t.CategoryGuid, t.DownloadGuid })
.ForeignKey("dbo.DownloadCategories", t => t.CategoryGuid)
.ForeignKey("dbo.Downloads", t => t.DownloadGuid, cascadeDelete: true)
.Index(t => t.CategoryGuid)
.Index(t => t.DownloadGuid);

这是我的问题: 你可能注意到其中一个外键没有添加 cascadeDelete: true。为什么啊!!!!???

我应该提到我没有更改任何 modelbuilder 的约定。 因此,此模式应在迁移中添加Casscade on delete。 我的属性为[Required]

我做错了什么?

谢谢大家...

更新: 请注意ArticleCategory类是abstract。 我更改了上面的类

更新2: 此模式不存在逻辑问题。 如果我手动编辑迁移,它将正常更新数据库。

更新3: 我的 EF 继承方法论是 TPC

更新4: 经过一些调查和测试,似乎问题是从Category继承而来的。 当DownloadCategoryCategory继承时,级联未部署。 但当我直接从 Article 继承 DownloadCategory 时,级联被部署。 可是为什么呢?


如果手动编辑迁移可以正常工作,那么这更多是一个关于为什么无法推断级联删除的理论问题吗? - jjj
5
@jjj 不。在我看来,这完全是可行的。手动编辑迁移根本不是一个好的做法。在像我正在处理的这样的大型项目中,这样做会增加对项目维护和开发新功能的更多考虑。我认为手动编辑迁移是错误的,迁移应该与模型的架构相匹配。 - Manoochehr Dadashi
4
在一个理想的世界中,如果内置惯例完美无缺,那么这个说法或许是正确的。你可以深入源代码,以找出为什么在这种情况下会发生这种情况,但我猜测这是为了避免循环而采取的一种捷径的结果。 - jjj
2
我认为如果需要移动数据,或者添加视图、存储过程等内容,您还需要编辑迁移。 - jjj
谢谢@jjj,我会在源代码中进行一些调查以确定主要原因。当我找到答案时,我会在这里更新。 - Manoochehr Dadashi
1
@abzarak,你在查找源代码时是否找到了解决方案?我也遇到了同样的问题,但无法找到原因。从代码优先模型生成的EDM(从__MigrationsHistory表中提取)甚至似乎包含适当的OnDelete Action="Cascade",但不知何故它没有传递给CreateForeignKeyOperation...而且确实似乎仅限于具有相对复杂继承链的类。 - lc.
1个回答

3

我认为这是因为:

DownloadCategory : Category : Article

对比

Download : Article

关键在于 Article 类。多个 DownloadCategories 可能使用相同的 Category,因此它不会在删除时级联,因为这可能会导致其他 DownloadCategory 损坏。
这可能是 Entity Framework 的一个失败,因为您正在使用 TPC,这应该被推断出来。请参阅 this article 了解解决方法。
具体来说,这些部分:
在大多数情况下,Entity Framework 可以推断出哪种类型是关系中的从属方和主要方。但是,当关系的两端都是必需的或两侧都是可选的时,Entity Framework 无法确定从属方和主要方。当关系的两端都是必需的时,请在 HasRequired 方法之后使用 WithRequiredPrincipal 或 WithRequiredDependent。当关系的两端都是可选的时,请在 HasOptional 方法之后使用 WithOptionalPrincipal 或 WithOptionalDependent。
// Configure the primary key for the OfficeAssignment 
modelBuilder.Entity<OfficeAssignment>() 
    .HasKey(t => t.InstructorID); 

modelBuilder.Entity<Instructor>() 
    .HasRequired(t => t.OfficeAssignment) 
    .WithRequiredPrincipal(t => t.Instructor);

您可以使用WillCascadeOnDelete方法在关系上配置级联删除。如果从属实体上的外键不可为空,则Code First将在关系上设置级联删除。如果从属实体上的外键可为空,则Code First不会在关系上设置级联删除,当主实体被删除时,外键将被设置为null。您可以使用以下方法删除这些级联删除约定:
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>()
modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>()

以下代码配置关系为必需,并禁用级联删除。
modelBuilder.Entity<Course>() 
    .HasRequired(t => t.Department) 
    .WithMany(t => t.Courses) 
    .HasForeignKey(d => d.DepartmentID) 
    .WillCascadeOnDelete(false);

文章和类别类是抽象的。我正在更新我的问题,指出它们是抽象的。谢谢。 - Manoochehr Dadashi
我不确定这会有什么区别? - Paul Coghill
如果Category类是抽象的,那么就不会有直接的Category实例。而实例来自DownloadCategory。因此,“多个DownloadCategories可以使用相同的Category”在这里没有意义... - Manoochehr Dadashi
同时,DownloadCategory是从Category继承而来的。我的EF继承方法论是TPC。@Paul Coghill - Manoochehr Dadashi
2
一对一关系项只是为了说明EF推断的限制。我认为它并不无关紧要,但请随意编辑答案以消除混淆。最初的问题似乎是询问为什么EF不能自动推断级联。然后您提到了抽象和TPC,因此我推断您想要一个解决方法。那么问题是使用解决方法部署迁移吗?这些是三个独立的问题。所有更新意味着其他人很难阅读这个问题。您能否考虑重新编写问题以考虑所有这些内容? - Paul Coghill
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接