实体框架:使用CodeFirst设置删除规则 [英] Entity Framework: Set Delete Rule with CodeFirst

查看:184
本文介绍了实体框架:使用CodeFirst设置删除规则的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用EF4 CTP 5,CodeFirst。



请先看看我的课程:

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

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

public class语言
{
[Key]
public Guid LanguageID {get;组; }

[必需(ErrorMessage =输入语言名称)]
[StringLength(50,ErrorMessage =语言名称太长)
public string LanguageName {get ;组; } // in origine language
}

我的目标是设置一个删除规则为客人语言关系。当语言被删除时,我不希望删除相应的邀请对象(所以级联删除)。相反,我希望客人的LanguageID为设置NULL。



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



感谢任何帮助!

解决方案

可以通过在客人语言实体之间设置可选关联 p>



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



单元测试:



< pre class =lang-cs prettyprint-override> 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();
}

因此,我们最终会有一个客人记录为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);
}

您正在寻找什么?有了上述种子方法,第二单位测试也将通过。



希望这有帮助,


Morteza


I am using EF4 CTP 5, CodeFirst.

Please see my classes first:

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
}

My goal is to set a certain "Delete Rule" for the Guest-Language relationship. When a language is deleted, I do not want to delete the corresponding guests (so NO cascade delete). Instead I want the guest's LanguageID to be "Set NULL".

I was hoping for the fluent API to support me here. But I couldn't find anything helpful besides .WillCascadeOnDelete(bool), which does not provide the options I need. Did I miss anything? Or is this just not implemented in CTP 5?

Thanks for any help!

解决方案

What you are looking for can be achieved by setting up an optional association between Guest and Language entities:

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

Unit Test:

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

As a result, we'll end up having a guest record with a DB null value for the LanguageID FK column.


Update:

First let's see why the above Unit Test succeeded by looking into SQL Profiler. The below shows the trace right after calling the second SaveChanges() method:

So as you can see, EF is smart enough to first update the guest record by setting its LanguageID to null and then submit a delete statement to remove the language record which is the default EF behavior when you set up an optional association. So it has been taken care of on the application side by EF and of course you'll get an error from the DBMS if you try to manually delete the language record inside the SQL Server like you also mentioned.

However, there is more to this story. Consider the following unit test:

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

This one fails with throwing a SQLException contains the exact message that you got from the SQL Server while trying to manually delete the record. The reason for that is because in the second unit test we do no have the related guest object loaded in the context so that EF is not aware of it and won't submit the necessary update statement like it did in the first example.

Back to your question, unfortunately EF Code First does not allow explicitly changing delete/update rule on relationships but we can always resort to SqlCommand method as you see an example of it in this post. In your case, we can code:

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

Which is what you are looking for. With having the above seed method in place, the second unit test will also pass.

Hope this helps,
Morteza

这篇关于实体框架:使用CodeFirst设置删除规则的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆