简体   繁体   English

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

[英]Entity Framework Core Code-First: Cascade delete on a many-to-many relationship

I'm working on an ASP.NET MVC 6 project with Entity-Framework Core (version "EntityFramework.Core": "7.0.0-rc1-final" ) backed by a SQL Server 2012 express DB. 我正在使用SQL Server 2012 Express DB支持的Entity-Framework Core(版本"EntityFramework.Core": "7.0.0-rc1-final" )的ASP.NET MVC 6项目。

I need to model a many-to-many relationship between a Person entity and an Address entity. 我需要模拟Person实体和Address实体之间的多对多关系。 As per this guide I modeled it with a PersonAddress join-table entity, because this way I can store some extra info. 根据指南,我使用PersonAddress连接表实体对其进行建模,因为这样我可以存储一些额外的信息。

My goal is to set-up my system this way: 我的目标是以这种方式设置我的系统:

  • If a Person instance is deleted, all the related PersonAddress instances must be deleted. 如果删除Person实例,则必须删除所有相关的PersonAddress实例。 All the Address instances they reference to must be deleted too, only if they are not related to other PersonAddress instances. 只有当它们与其他PersonAddress实例无关时,它们引用的所有Address实例也必须被删除。
  • If a PersonAddress instance is deleted, the Address instance it relates to must be deleted only if it is not related to other PersonAddress instances. 如果PersonAddress实例被删除,该Address涉及实例必须只有当它不与其他删除PersonAddress实例。 All Person instances must live. 所有Person实例都必须存在。
  • If an Address instance is deleted, all the related PersonAddress instances must be deleted. 如果删除了Address实例,则必须删除所有相关的PersonAddress实例。 All Person instances must live. 所有Person实例都必须存在。

I think most of the work must be done in the many-to-many relationship between Person and Address , but I expect to write some logic too. 我认为大多数工作必须在PersonAddress之间的多对多关系中完成,但我希望也写一些逻辑。 I will leave this part out of this question. 我将把这一部分从这个问题中解脱出来。 What I'm interested in is how to configure my many-to-many relationship. 我感兴趣的是如何配置我的多对多关系。

Here is the current situation . 这是目前的情况

This is the Person entity. 这是Person实体。 Please note that this entity has got one-to-many relationships with other secondary entities. 请注意,此实体与其他辅助实体具有一对多关系。

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
}

This is the Address entity. 这是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
}

This is the PersonAddress entity. 这是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
}

This is the DatabaseContext entity, where all the relationships are described. 这是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 entities have been removed for the sake of simplicity. 为简单起见, TelephoneCity实体都已删除。

This is the code for removing a Person . 这是删除Person的代码。

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

}

As for my readings avoiding .Include() will let the DB take care of the eventual CASCADE deletes. 至于我的读数,避免.Include()将让DB处理最终的CASCADE删除。 I'm sorry but I don't remember the SO question where this concept was clarified. 对不起,但我不记得澄清这个概念的SO问题。

If I run this code I can seed the DB using this workaround . 如果我运行此代码,我可以使用此解决方法为数据库播种。 When I want to test-deleting a Person entity with the above code, I get this exception: 当我想使用上面的代码测试删除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.

I tested several relationship setups in the DatabaseContext.OnModelCreating method without any luck. 我在DatabaseContext.OnModelCreating方法中测试了几个关系设置而没有任何运气。

Finally, here's my question . 最后,这是我的问题 How should I configure my many-to-many relationship in order to correctly delete a Person and its related entities from my application, according to the goal described before? 根据之前描述的目标 ,我应该如何配置我的多对多关系以便从我的应用程序中正确删除Person及其相关实体?

Thank you all. 谢谢你们。

First I see you have set City and Address relationship with DeleteBehavior.Restrict and you say: ' //I don't want to delete the City when I delete an Address '. 首先,我看到你已经设置了与DeleteBehavior.Restrict 城市地址关系,你说:' //我删除地址时不想删除城市 '。
But you don't need Restrict here, because even with DeleteBehavior.Cascade City will not be deleted. 但是你不需要在这里限制,因为即使使用DeleteBehavior.Cascade City也不会被删除。 You are looking it from the wrong side. 你是从错误的一面看。 What Cascade here does is when a City is deleted all addresses belonging to it are also deleted. Cascade在这里做的是删除城市时,所有属于它的地址也会被删除。 And that behavour is logical. 而这种行为是合乎逻辑的。

Secondly your many-to-many relationship is fine. 其次你的多对多关系很好。 When deleting Person its links from PersonAddress Table will automatically be deleted because of Cascade. 删除Person时,由于Cascade,将自动删除PersonAddress Table中的链接。 And if you want also to delete Addresses that were connected only to that Person you will have to do it manually. 如果您还想删除仅与该Person连接的地址,则必须手动执行此操作。 You actually have to delete those Addresses before deleting Person is order to know what to delete. 在删除Person之前,您实际上必须删除这些地址,以便知道要删除的内容。
So logic should be following: 所以逻辑应遵循:
1. Query through all record of PersonAddress where PersonId = person.Id ; 1.查询PersonAddress的所有记录,其中PersonId = person.Id ;
2. Of those take only ones that have single occurance of AddressId in PersonAddress table, and delete them from Person table. 2.那些只接受在PersonAddress表中单独出现AddressId的那些,并从Person表中删除它们。
3. Now delete the Person. 3.现在删除Person。

You could do this in code directly, or if you want database to do it for you, trigger could be created for step 2 with function: When row from PersonAddress is about to be deleted check if there are no more rows with same AddressId in that PersonAddress table in which case delete it from Address table. 您可以直接在代码中执行此操作,或者如果您希望数据库为您执行此操作,可以使用函数为步骤2创建触发器:当要删除PersonAddress中的行时,检查是否没有更多具有相同AddressId的行PersonAddress表,在这种情况下从Address表中删除它。

More info here: 更多信息:
How to cascade delete over many to many table 如何级联删除多对多表
How do I delete from multiple tables using INNER JOIN in SQL server 如何在SQL Server中使用INNER JOIN从多个表中删除

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 实体框架核心代码优先-0..1至多关系和级联删除 - Entity Framework Core code first - 0..1 to many relationship and cascade delete 多对多关系中不完整的EF代码优先级联删除 - Incomplete EF code-first cascade delete on many to many relationship 通过具有多对多关系的实体框架代码优先方法为 SQL Server 播种 - Seeding SQL Server by Entity Framework code-first approach with many-to-many relationship 实体框架,代码优先,如何从多对多关系映射中向表添加主键? - Entity Framework, code-first, how to add primary key to table out of many-to-many relationship mapping? 代码优先实体框架-多对多关系 - Code-first Entity Framework - many-to-many relationships 连接表中的实体框架核心代码优先多对多属性 - Entity Framework Core code-first many-to-many properties in join table 实体框架代码优先在同一个表上的多对多关系 - Entity Framework Code-first Many to many relationship on the same table Entity Framework 4.1 - Code First:多对多关系 - Entity Framework 4.1 - Code First: many-to-many relationship 如何使3个实体之间的一对多关系与Entity Framework Core 2.1一起使用 - 代码优先 - How to make one-to-many relationship between 3 entities work with Entity Framework Core 2.1 - code-first 实体框架核心级联删除一对多关系 - Entity Framework Core cascade delete one to many relationship
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM