用于一对零或一关系的实体框架 (EF) 代码优先级联删除

Posted

技术标签:

【中文标题】用于一对零或一关系的实体框架 (EF) 代码优先级联删除【英文标题】:Entity Framework (EF) Code First Cascade Delete for One-to-Zero-or-One relationship 【发布时间】:2013-07-03 11:30:51 【问题描述】:

按照Pluralsight "Getting Started with Entity Framework 5" course by Julie Lerman 的“代码优先建模”部分,我创建了两个具有一对零或一 关系的 POCO 类:一个父级(用户)和一个 可选子(UserDetail)。

User and UserDetail data model diagram (click to view).

请注意图中UserId 属性是UserDetail 的主键和外键

相关代码:

public class User

    //...

    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int UserId  get; set; 

    /* Has a 1:0..1 relationship with UserDetail */
    public virtual UserDetail UserDetail  get; set; 

    //...


public class UserDetail

    //...

    /* Has a 0..1:1 relationship with User */
    public virtual User User  get; set; 

    [Key, ForeignKey("User")]
    public int UserId  get; set; 

    //...


public class EFDbContext : DbContext

    public DbSet<User> Users  get; set; 
    //public DbSet<UserDetail> UserDetails  get; set;   /* Explicit declaration not necessary. Context is aware of UserDetail entity due to 0..1:1 relationship with User */

    public EFDbContext()
    
        Configuration.ProxyCreationEnabled = true;
        Configuration.LazyLoadingEnabled = true;
    


public class UserRepository : IUserRepository

    private EFDbContext _context = new EFDbContext();

    public void Delete(User entity)
    
        entity = _context.Users.Find(entity.UserId);

        //...

        _context.Users.Remove(entity);
        _context.SaveChanges();

        //...
    

调用UserRepository类中的Delete()方法时,不会删除数据库中的User记录,因为UserDetail中的外键没有级联删除。

DELETE 语句与 REFERENCE 约束“FK_dbo.UserDetail_dbo.User_UserId”冲突。

如何使用实体框架代码优先为一对零或一关系启用级联删除(以便删除用户自动删除 UserDetail)?

【问题讨论】:

【参考方案1】:

您必须使用 fluent API 来执行此操作。

尝试将以下内容添加到您的DbContext

protected override void OnModelCreating(DbModelBuilder modelBuilder)
   
    modelBuilder.Entity<User>()
        .HasOptional(a => a.UserDetail)
        .WithOptionalDependent()
        .WillCascadeOnDelete(true);

【讨论】:

WillCascadeOnDelete() 是做什么的,虽然我必须将它应用于依赖表(UserDetail),而不是这种关系中的主体(User)。此外,我删除了 Key 和 ForeignKey 数据通知在 UserDetail 类中的 UserId 属性上。非常感谢! FWIW,这篇文章帮助我了解了将 WillCascadeOnDelete() 应用于依赖表 msdn.microsoft.com/en-us/data/jj591620.aspx#RequiredToOptional 的想法。 正是我需要的。有些人建议[必需]进行级联删除。这确实有效,但当然,只有在实际需要时。 没有办法用注解代替流畅的API吗? @Rosdi 你读过你上面的评论了吗?如果您不介意 fk 不可为空,则可以使用 [Required] 属性。【参考方案2】:

您还可以通过执行以下操作在应用程序的全局范围内禁用级联删除约定:

modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>()
modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>()

【讨论】:

这为我节省了很多时间【参考方案3】:

这段代码对我有用

protected override void OnModelCreating(DbModelBuilder modelBuilder)
    
        modelBuilder.Entity<UserDetail>()
            .HasRequired(d => d.User)
            .WithOptional(u => u.UserDetail)
            .WillCascadeOnDelete(true);
    

迁移代码是:

public override void Up()
    
        AddForeignKey("UserDetail", "UserId", "User", "UserId", cascadeDelete: true);
    

而且效果很好。当我第一次使用时

modelBuilder.Entity<User>()
    .HasOptional(a => a.UserDetail)
    .WithOptionalDependent()
    .WillCascadeOnDelete(true);

迁移代码是:

AddForeignKey("User", "UserDetail_UserId", "UserDetail", "UserId", cascadeDelete: true); 

但它不匹配任何两个可用的重载(在 EntityFramework 6 中)

【讨论】:

以上是关于用于一对零或一关系的实体框架 (EF) 代码优先级联删除的主要内容,如果未能解决你的问题,请参考以下文章

一对零或一关系实体框架

在 SQL Server 中实现一对零或一关系

将 sonata_type_collection 用于一对多一关系的问题

实体代码第一个零或一到零或一关系

使用 CodeFirst 的 C# 一对零或一关系

EF6基础系列(五)---EF中的实体关系