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

2024-02-14

我正在使用 Entity-Framework Core(版本"EntityFramework.Core": "7.0.0-rc1-final")由 SQL Server 2012 Express DB 支持。

我需要建立一个多对多关系模型Person实体和一个Address实体。 按照this http://ef.readthedocs.org/en/latest/modeling/relationships.html#many-to-many指南我用它建模了PersonAddressjoin-table 实体,因为这样我可以存储一些额外的信息。

My goal就是这样设置我的系统:

  • If a Person实例被删除,所有相关的PersonAddress必须删除实例。一切Address它们引用的实例也必须被删除,前提是它们与其他实例无关PersonAddress实例。
  • If a PersonAddress实例被删除,则Address仅当与其他实例不相关时,才必须删除与其相关的实例PersonAddress实例。全部Person实例必须存在。
  • If an Address实例被删除,所有相关的PersonAddress必须删除实例。全部Person实例必须存在。

我认为大部分工作必须在多对多的关系中完成Person and Address,但我也希望写一些逻辑。我将把这部分排除在这个问题之外。我感兴趣的是如何配置我的多对多关系。

这是当前的情况.

这是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 entity.

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 entity.

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

Both Telephone and City为了简单起见,实体已被删除。

这是删除一个的代码Person.

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

}

至于我的阅读避免.Include()将让数据库处理最终的事情CASCADE删除。抱歉,但我不记得澄清这个概念的 SO 问题。

如果我运行此代码,我可以使用以下方式为数据库播种这个解决方法 https://stackoverflow.com/a/35798149/6011874。当我想测试删除一个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方法没有任何运气。

最后,这是我的question。我应该如何配置多对多关系才能正确删除Person及其来自我的申请的相关实体,根据goal之前描述过?

谢谢你们。


首先我看到你已经设置了City and Address有关系DeleteBehavior.Restrict你说: '//删除地址时我不想删除城市'.
但你不需要在这里限制,因为即使DeleteBehavior.Cascade城市不会被删除。 你从错误的角度看问题。 什么Cascade这里的意思是,当删除一个城市时,属于该城市的所有地址也会被删除。 这种行为是合乎逻辑的。

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

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

更多信息请点击这里:
如何对多对多表进行级联删除 https://stackoverflow.com/questions/1379333/how-to-cascade-delete-over-many-to-many-table
如何在 SQL Server 中使用 INNER JOIN 从多个表中删除 https://stackoverflow.com/questions/783726/how-do-i-delete-from-multiple-tables-using-inner-join-in-sql-server

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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