【问题标题】:Incomplete EF code-first cascade delete on many to many relationship多对多关系上的不完整 EF 代码优先级联删除
【发布时间】:2014-04-30 21:26:27
【问题描述】:

我有一个PhoneNumber 实体,我想在多个实体中引用它。例如,Contact 实体具有多个 PhoneNumber,Business 实体具有一个 PhoneNumber。

public class PhoneNumber
{
    [Key, Column(Order = 0), DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }
    public string Number { get; set; }
}

public class Contact
{
    [Key, Column(Order = 0), DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    public virtual ICollection<PhoneNumber> PhoneNumbers { get; set; }
}

public class Business
{
    [Key, Column(Order = 0), DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    [ForeignKey("PhoneNumber")]
    public int PhoneNumberId { get; set; }
    public virtual PhoneNumber PhoneNumber { get; set; }        
}

我设置了ContactBusiness,以便它们具有单向导航属性。此外,电话号码是可选的。我还为Contact 设置了许多关系,以防止EF 在我添加迁移时添加Contact_Id 列。映射如下(注意使用WithMany(),因为PhoneNumber没有返回Contact的导航属性):

modelBuilder.Entity<Contact>().HasMany(r => r.PhoneNumbers).WithMany()
            .Map(x => x.MapLeftKey("ContactId").MapRightKey("PhoneId"));

当我添加带有多个电话号码的Contact 时,它会被很好地添加。有ContactPhoneNumbers 表和ContactPhoneNumbers 链接表的记录。

但是,我遇到的问题是删除联系人时。 EF 正确删除了ContactPhoneNumbers 链接表中的条目和Contact 条目,但它不会从PhoneNumbers 表中删除条目。我已经看到使用modelBuilder 进行映射的示例,其中使用了WillCascadeOnDelete(true),但是在使用WithMany() 时该选项不可用。

我需要做什么才能使这种级联删除正常工作?这个设置可以吗?或者我是否需要为每个实体(联系人和业务)设置一个单独的 PhoneNumbers 表来设置相应的 PhoneNumber 表使用 FK(例如,Contact_Id)的关系?

我对 EF 还很陌生,因此欢迎提出任何建议。我可能完全错了。

编辑:这是相关的迁移代码...

CreateTable(
    "dbo.PhoneNumbers",
    c => new
        {
            Id = c.Int(nullable: false, identity: true),
            Number = c.String()
        })
    .PrimaryKey(t => t.Id);

CreateTable(
    "dbo.Contacts",
    c => new
        {
            Id = c.Int(nullable: false, identity: true)
        })
    .PrimaryKey(t => t.Id);

CreateTable(
    "dbo.ContactPhoneNumbers",
    c => new
        {
            ContactId = c.Int(nullable: false),
            PhoneId = c.Int(nullable: false),
        })
    .PrimaryKey(t => new { t.ContactId, t.PhoneId })
    .ForeignKey("dbo.Contacts", t => t.ContactId, cascadeDelete: true)
    .ForeignKey("dbo.PhoneNumbers", t => t.PhoneId, cascadeDelete: true)
    .Index(t => t.ContactId)
    .Index(t => t.PhoneId);

CreateTable(
    "dbo.Business",
    c => new
        {
            Id = c.Int(nullable: false),
            PhoneNumberId = c.Int(nullable: false)
        })
    .PrimaryKey(t => t.Id)
    .ForeignKey("dbo.PhoneNumbers", t => t.PhoneNumberId, cascadeDelete: true)
    .Index(t => t.Id)
    .Index(t => t.PhoneNumberId);

【问题讨论】:

  • 我没有看到任何关于 ContactPhoneNumbers 表的信息。从您的代码中,Contact 似乎具有指向电话号码表的直接链接。您可以发布 ContactPhone 号码的映射吗?
  • @mmilleruva 这是我的帖子中modelBuilder 映射结果由EF 迁移创建的链接表。我实际上并没有自己直接创建它。
  • @mmilleruva 编辑帖子以添加迁移代码

标签: c# .net entity-framework ef-code-first entity-framework-6


【解决方案1】:

为了进行级联删除工作,将被级联的记录必须具有指向被删除记录的外键。因此,在您的示例中,您删除了联系人记录。因为它们是从 ContactPhoneNumber 到 Contact 的外键,所以级联工作。由于没有从 PhoneNumber 到 ContactPhoneNumber 的外键,(外键相反)级联不会继续。

这是因为您将关系定义为多对多。如果您考虑尝试根据需要在模型上执行级联删除,如果删除了 ContactPhoneNumber,然后删除了其关联的 PhoneNumber,那么现在可能存在其他没有有效 PhoneNumber 的 ContactPhoneNumber(因为可以多个 ContactPhoneNumbers 到一个 PhoneNumber)。现在需要删除这些,并且此过程将继续。数据库不喜欢这种循环级联。

我并不完全清楚为什么需要多对多关系,如果你真的需要它,你将无法在删除时执行级联。如果你能建立你的关系:

1 Contact - * ContactPhoneNumber 1- * PhoneNumber,然后您也可以将级联配置为您想要的工作方式。

【讨论】:

  • 谢谢,这是有道理的,也是我期望 many:many 工作的方式。我想我看到架构设置正在运行,并认为当我使用 WithMany() 时它是正确的(即,PhoneNumbers 上没有导航属性并且能够从 BusinessContact 使用它)。也就是说,你是对的,它目前是很多:很多,但我的意图是它是 1:很多。我将不得不更改它,并且可能使用一个单独的表,其中 PhoneNumber 是 1:1 的其他实体(例如,Business)。
猜你喜欢
  • 2012-01-09
  • 1970-01-01
  • 2016-04-11
  • 2016-06-18
  • 1970-01-01
  • 1970-01-01
  • 2013-05-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多