21

我正在使用 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
}

我的目标是为客户语言关系设置一定的“删除规则”。当一种语言被删除时,我不想删除相应的客人(所以没有级联删除)。相反,我希望客人的 LanguageID 为“Set NULL”。

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

谢谢你的帮助!

4

2 回答 2

37

您正在寻找的可以通过在和实体之间设置可选关联来实现: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 空值的记录。


更新:

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

在此处输入图像描述 在此处输入图像描述

如您所见,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 获得的确切消息。这样做的原因是因为在第二个单元测试中,我们没有在上下文中加载相关的来宾对象,因此 EF 不知道它并且不会像在第一个示例中那样提交必要的更新语句。

回到您的问题,不幸的是,EF Code First 不允许显式更改关系上的删除/更新规则,但我们始终可以使用SqlCommand方法,正如您在这篇文章中看到的示例一样。在您的情况下,我们可以编码:

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");
}

这就是你要找的。有了上述种子方法,第二个单元测试也将通过。

希望这会有所帮助,
莫特萨

于 2011-02-19T14:57:06.803 回答
0

使用 Entity Framework Core,我做了这样的事情......

modelBuilder.Entity<Guest>()
   .HasOne(g => g.Language)
   .WithMany(l => l.Guests) // I needed a return collection
   .OnDelete(DeleteBehavior.SetNull);
于 2018-02-01T23:17:54.000 回答