【问题标题】:Entity Framework Core Code-First: Cascade delete on a many-to-many relationship实体框架核心代码优先:多对多关系上的级联删除
【发布时间】:2016-06-18 09:49:27
【问题描述】:

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

我需要在 Person 实体和 Address 实体之间建模多对多关系。 根据this 指南,我使用PersonAddressjoin-table 实体对其进行建模,因为这样我可以存储一些额外的信息。

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

  • 如果删除了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() 将让数据库处理最终的 CASCADE 删除。很抱歉,但我不记得澄清这个概念的 SO 问题。

如果我运行此代码,我可以使用this workaround 为数据库播种。当我想用上面的代码测试删除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 及其相关实体?

谢谢大家。

【问题讨论】:

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


    【解决方案1】:

    首先我看到你设置了 CityAddressDeleteBehavior.Restrict 的关系,你说: '//我不想在删除地址时删除城市'。
    但是这里不需要 Restrict,因为即使使用DeleteBehavior.Cascade City 也不会被删除。 你从错误的角度看它。 Cascade 这里所做的是,当一个城市被删除时,属于它的所有地址也会被删除。 这种行为是合乎逻辑的。

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

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

    更多信息在这里:
    How to cascade delete over many to many table
    How do I delete from multiple tables using INNER JOIN in SQL server

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-05-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多