实体框架:使用代码优先设置删除规则

时间:2011-02-19 03:02:42

标签: entity-framework-4 entity-framework-ctp5 cascading-deletes

我正在使用EF4 CTP 5,CodeFirst。

请先看我的课程:

public class Guest
{
        [Key]
        public Guid GuestID { get; set; }

        public Language PreferredLanguage { get; set; }
        public Guid? LanguageID { get; set; }
}

public class Language
{
        [Key]
        public Guid LanguageID { get; set; }

        [Required(ErrorMessage = "Enter language name")]
        [StringLength(50, ErrorMessage = "Language name is too long")]
        public string LanguageName { get; set; } // in origine language
}

我的目标是为访客语言关系设置一定的“删除规则”。删除语言后,我不希望删除相应的guest虚拟机(因此 NO 级联删除)。相反,我希望客人的LanguageID为“Set NULL”。

我希望流利的API在这里支持我。但我找不到任何有用的东西.WillCascadeOnDelete(bool),它不提供我需要的选项。我错过了什么吗?或者这只是在CTP 5中没有实现?

感谢您的帮助!

2 个答案:

答案 0 :(得分:34)

您可以通过在GuestLanguage个实体之间设置可选关联来实现您的目标:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Guest>()
                .HasOptional(p => p.PreferredLanguage)
                .WithMany()
                .HasForeignKey(p => p.LanguageID);
}

单元测试:

using (var context = new Context())
{
    var language = new Language()
    {
        LanguageName = "en"
    };
    var guest = new Guest()
    {
        PreferredLanguage = language
    };
    context.Guests.Add(guest);
    context.SaveChanges();

    context.Languages.Remove(language);
    context.SaveChanges();
}

因此,我们最终会得到guest记录,其中包含LanguageID FK列的DB null值。


更新

首先让我们看看为什么上面的单元测试通过查看SQL事件探查器而成功。下面显示了调用第二个SaveChanges()方法后的跟踪:

enter image description here enter image description here

正如您所看到的,EF足够聪明,首先通过将其LanguageID设置为null来更新来宾记录,然后提交删除语句以删除语言记录,这是您设置时的默认EF行为可选的关联。所以它在EF的应用程序方面得到了解决,当然如果您尝试手动删除SQL Server中的语言记录,您将从DBMS中收到错误,就像您也提到的那样。

但是,这个故事还有更多。考虑以下单元测试:

using (var context = new Context())
{
    var language = new Language() { LanguageName = "en" };
    var guest = new Guest() { PreferredLanguage = language };
    context.Guests.Add(guest);
    context.SaveChanges();
}

using (var context = new Context())
{
    var language = context.Languages.First();        
    context.Languages.Remove(language);
    context.SaveChanges();
}     

这个失败了抛出一个SQLException包含你在尝试手动删除记录时从SQL Server获得的确切消息。原因是因为在第二个单元测试中我们没有在上下文中加载相关的guest对象,因此EF不知道它并且不会像在第一个示例中那样提交必要的更新语句。

回到你的问题,遗憾的是EF Code First不允许显式更改关系上的删除/更新规则,但我们总是可以使用 SqlCommand 方法,因为你在{{3}中看到了它的示例}。在您的情况下,我们可以编码:

protected override void Seed(Context context)
{
    context.Database.SqlCommand("ALTER TABLE dbo.Guests DROP CONSTRAINT Guest_PreferredLanguage");
    context.Database.SqlCommand("ALTER TABLE dbo.Guests ADD CONSTRAINT Guest_PreferredLanguage FOREIGN KEY (LanguageID) REFERENCES dbo.Languages(LanguageID) ON UPDATE NO ACTION ON DELETE SET NULL");
}

您正在寻找的是什么。如果采用上述种子方法,第二次单元测试也将通过。

希望这有帮助,
Morteza

答案 1 :(得分:0)

使用Entity Framework Core,我做了类似的事情......

modelBuilder.Entity<Guest>()
   .HasOne(g => g.Language)
   .WithMany(l => l.Guests) // I needed a return collection
   .OnDelete(DeleteBehavior.SetNull);