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