8

我正在使用由SQL Server 2012 express数据库支持的Entity-Framework Core(版本号"EntityFramework.Core": "7.0.0-rc1-final")开发ASP.NET MVC 6项目。实体框架核心代码 - 第一:在多对多关系上级联删除

我需要建模一个Person实体和一个Address实体之间的多对多关系。 根据this指南,我使用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(); 
    } 
} 

Telephone都和City实体已经为简单起见移除。

这是删除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删除的照顾。我很抱歉,但我不记得这个概念澄清的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及其相关实体?

谢谢大家。

回答

0

首先,我看你已经设置地址关系与DeleteBehavior.Restrict和你说: “//我不想删除时,我删除地址的城市”。
但是您不需要在此限制,因为即使使用DeleteBehavior.Cascade城市也不会被删除。 你正从错误的一面看它。 什么Cascade这里做的是当一个城市被删除时,所有属于它的地址也被删除。 而这种行为是合乎逻辑的。

其次你的多对多关系很好。 删除Person时,由于Cascade会自动删除PersonAddress Table中的链接。 如果您还想删除仅连接到该人的地址,则必须手动执行。 删除之前,您实际上必须删除这些地址Person是为了知道要删除的内容。
所以逻辑应该如下:
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

相关问题