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

m.p*_*bos 10 c# entity-framework code-first ef-code-first entity-framework-core

我正在使用"EntityFramework.Core": "7.0.0-rc1-final"由SQL Server 2012 Express DB支持的Entity-Framework Core(版本)的ASP.NET MVC 6项目.

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

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

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

我认为大多数工作必须在Person和之间的多对多关系中完成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
}
Run Code Online (Sandbox Code Playgroud)

这是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
}
Run Code Online (Sandbox Code Playgroud)

这是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
}
Run Code Online (Sandbox Code Playgroud)

这是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();
    }
}
Run Code Online (Sandbox Code Playgroud)

为简单起见,已删除了两者TelephoneCity实体.

这是删除a的代码Person.

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

}
Run Code Online (Sandbox Code Playgroud)

至于我的阅读,避免.Include()让DB负责最终的CASCADE删除.对不起,但我不记得澄清这个概念的SO问题.

如果我运行此代码,我可以使用此解决方法为数据库播种.当我想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.
Run Code Online (Sandbox Code Playgroud)

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

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

谢谢你们.

bor*_*sdj 5

首先我看到你已经设置了城市地址关系,DeleteBehavior.Restrict然后你说:' //我不想在删除地址时删除城市'。
但是这里不需要Restrict,因为即使有DeleteBehavior.CascadeCity也不会被删除。你是从错误的一边看的。什么Cascade当一个城市被删除属于它的所有地址也将被删除这里确实是。这种行为是合乎逻辑的。

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

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

更多信息:
如何在多对多表上级联删除
如何在 SQL 服务器中使用 INNER JOIN 从多个表中删除