实体框架核心代码优先:多对多关系中的级联删除

时间:2016-03-04 15:29:08

标签: c# entity-framework ef-code-first code-first entity-framework-core

我正在开发一个带有Entity-Framework Core(版本"EntityFramework.Core": "7.0.0-rc1-final")的ASP.NET MVC 6项目,该项目由SQL Server 2012 Express DB支持。

我需要对Person实体和Address实体之间的多对多关系进行建模。 根据{{​​3}}指南,我使用PersonAddress连接表实体对其进行建模,因为这样我可以存储一些额外的信息。

我的目标是以这种方式设置我的系统:

  • 如果删除Person实例,则必须删除所有相关的PersonAddress实例。他们引用的所有Address个实例也必须删除,只要它们与其他PersonAddress个实例无关。
  • 如果删除PersonAddress实例,则只有在与其他Address实例无关时,才必须删除与其相关的PersonAddress实例。所有Person个实例都必须存在。
  • 如果删除Address实例,则必须删除所有相关的PersonAddress实例。所有Person个实例都必须存在。

我认为大多数工作必须在PersonAddress之间的多对多关系中完成,但我希望也写一些逻辑。我将把这一部分从这个问题中解脱出来。我感兴趣的是如何配置我的多对多关系。

以下是当前的情况

这是Person实体。请注意,该实体与其他次要实体之间存在一对多的关系。

public class Person
{
    public int Id {get; set; } //PK
    public virtual ICollection<Telephone> Telephones { get; set; } //navigation property
    public virtual ICollection<PersonAddress> Addresses { get; set; } //navigation property for the many-to-many relationship
}

这是Address实体。

public class Address
{
    public int Id { get; set; } //PK
    public int CityId { get; set; } //FK
    public City City { get; set; } //navigation property
    public virtual ICollection<PersonAddress> People { get; set; } //navigation property
}

这是PersonAddress实体。

public class PersonAddress
{
    //PK: PersonId + AddressId
    public int PersonId { get; set; } //FK
    public Person Person {get; set; } //navigation property
    public int AddressId { get; set; } //FK
    public Address Address {get; set; } //navigation property
    //other info removed for simplicity
}

这是DatabaseContext实体,其中描述了所有关系。

public class DataBaseContext : DbContext
{
    public DbSet<Person> People { get; set; }
    public DbSet<Address> Addresses { get; set; }

    protected override void OnModelCreating(ModelBuilder builder)
    {            
        //All the telephones must be deleteded alongside a Person.
        //Deleting a telephone must not delete the person it refers to.
        builder.Entity<Person>()
            .HasMany(p => p.Telephones)
            .WithOne(p => p.Person);

        //I don't want to delete the City when I delete an Address
        builder.Entity<Address>()
            .HasOne(p => p.City)
            .WithMany(p => p.Addresses)
            .IsRequired().OnDelete(Microsoft.Data.Entity.Metadata.DeleteBehavior.Restrict);

        //PK for the join entity
        builder.Entity<PersonAddress>()
            .HasKey(x => new { x.AddressId, x.PersonId });

        builder.Entity<PersonAddress>()
            .HasOne(p => p.Person)
            .WithMany(p => p.Addresses)
            .IsRequired();

        builder.Entity<PersonAddress>()
            .HasOne(p => p.Address)
            .WithMany(p => p.People)
            .IsRequired();
    }
}

为简单起见,已删除TelephoneCity个实体。

这是删除Person

的代码
Person person = await _context.People.SingleAsync(m => m.Id == id);
try
{
    _context.People.Remove(person);
    await _context.SaveChangesAsync();
}
catch (Exception ex)
{

}

至于我的读数,避免.Include()将让DB处理最终的CASCADE删除。对不起,我不记得澄清这个概念的问题。

如果我运行此代码,我可以使用this为数据库播种。当我想用上面的代码测试删除Person实体时,我得到了这个例外:

The DELETE statement conflicted with the REFERENCE constraint "FK_PersonAddress_Person_PersonId". The conflict occurred in database "<dbName>", table "<dbo>.PersonAddress", column 'PersonId'.
The statement has been terminated.

我在DatabaseContext.OnModelCreating方法中测试了几个关系设置而没有任何运气。

最后,这是我的问题。我应该如何配置多对多关系,以便根据之前描述的目标从我的应用程序中正确删除Person及其相关实体?

谢谢大家。

1 个答案:

答案 0 :(得分:2)

首先,我看到您已将{strong>城市和地址DeleteBehavior.Restrict设置为关系,您说: &#39; //我在删除地址时不想删除城市&#39;。
但是,您不需要在此限制,因为即使DeleteBehavior.Cascade城市也不会被删除。 你是从错误的一面看。 这里Cascade的作用是删除城市时,所有属于它的地址也会被删除。 这种行为是合乎逻辑的。

其次你的多对多关系很好。 删除Person时,由于Cascade,将自动删除PersonAddress Table中的链接。 如果您还想删除仅与该Person连接的地址,则必须手动执行此操作。 在删除Person之前,您实际上必须删除这些地址,以便知道要删除的内容 所以逻辑应遵循:
1.查询PersonAddress的所有记录PersonId = person.Id;
2.那些只接受在PersonAddress表中单独出现AddressId的那些,并从Person表中删除它们 3.现在删除人。

您可以直接在代码中执行此操作,或者如果您希望数据库为您执行此操作,可以使用以下函数为步骤2创建触发器: 当要删除PersonAddress中的行时,检查该PersonAddress表中是否没有更多具有相同AddressId的行,在这种情况下从地址表中删除它。

此处有更多信息:
How to cascade delete over many to many table
How do I delete from multiple tables using INNER JOIN in SQL server