8

Sto lavorando a un progetto ASP.NET MVC 6 con Entity-Framework Core (versione "EntityFramework.Core": "7.0.0-rc1-final") supportato da un DB Express di SQL Server 2012.Codice nucleo di Entity Framework: prima eliminazione in cascata su una relazione molti-a-molti

Ho bisogno di modellare una relazione molti-a-molti tra un'entità Person e un'entità Address. Come da guida this l'ho modellato con un'entità join-table PersonAddress, perché in questo modo posso memorizzare alcune informazioni extra.

Il mio obiettivo è di set-up il mio sistema in questo modo:

  • Se un'istanza Person viene eliminato, tutti i relativi PersonAddress casi deve essere cancellato. È necessario eliminare anche tutte le istanze di Address a cui fanno riferimento, solo se non sono correlate ad altre istanze di PersonAddress.
  • Se un'istanza PersonAddress viene cancellata, l'istanza Address a cui si riferisce deve essere cancellata solo se non è collegata ad altre istanze PersonAddress. Tutte le istanze Person devono vivere.
  • Se un'istanza Address viene cancellata, tutte le relative istanze PersonAddress devono essere eliminate. Tutte le istanze Person devono vivere.

penso la maggior parte del lavoro deve essere fatto in molti-a-molti tra Person e Address, ma mi aspetto di scrivere una logica troppo. Lascerò questa parte fuori da questa domanda. Quello che mi interessa è come configurare la mia relazione molti-a-molti.

Questa è la situazione attuale .

Questa è l'entità Person. Si noti che questa entità ha relazioni uno-a-molti con altre entità secondarie.

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 
} 

Questa è l'entità 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 
} 

Questa è l'entità 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 
} 

Questa è la DatabaseContext entità, in cui sono descritte tutte le relazioni.

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(); 
    } 
} 

Sia Telephone e City entità sono state rimosse per semplicità.

Questo è il codice per la rimozione di Person.

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

} 

Per quanto riguarda le mie letture, evitando .Include() farà si che il DB prendersi cura degli eventuali CASCADE eliminazioni. Mi dispiace ma non ricordo la domanda SO in cui questo concetto è stato chiarito.

Se si esegue questo codice, è possibile inizializzare il DB utilizzando this workaround.Quando voglio testare-eliminazione di un'entità Person con il codice di cui sopra, ottengo questa eccezione:

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. 

ho provato diverse configurazioni relazionali nel metodo DatabaseContext.OnModelCreating senza alcuna fortuna.

Infine, ecco la mia domanda . Come dovrei configurare la mia relazione molti-a-molti al fine di eliminare correttamente un Person e le sue entità correlate dalla mia applicazione, secondo l'obiettivo descritto in precedenza ?

Grazie a tutti.

risposta

0

In primo luogo vedo che avete impostato Città e Indirizzo rapporto con DeleteBehavior.Restrict e tu dici: '// Non voglio cancellare la città quando elimino un indirizzo'.
Ma non è necessario Limitare qui, perché anche con la città DeleteBehavior.Cascade non verrà eliminato. Stai guardando dal lato sbagliato. Quello che fa Cascade è quando una città viene cancellata anche tutti gli indirizzi che appartengono ad essa vengono cancellati. E questo è logico.

In secondo luogo la vostra relazione molti-a-molti va bene. Durante l'eliminazione di Person i suoi collegamenti da PersonAddress Table verranno automaticamente cancellati a causa di Cascade. E se vuoi anche eliminare gli indirizzi che sono stati collegati solo a quella persona dovrai farlo manualmente. In realtà è necessario eliminare quegli indirizzi prima di eliminare la persona è l'ordine per sapere cosa eliminare.
Quindi la logica dovrebbe essere seguente:
1. Query attraverso tutti record di PersonAddress dove PersonId = person.Id;
2. Di quelli prendono solo quelli che hanno singola occorrenza di AddressId nella tabella PersonAddress ed eliminarli dalla tabella Persona.
3. Ora cancella la persona.

Si potrebbe fare questo nel codice direttamente, o se si vuole database per farlo per voi, grilletto potrebbe essere creato per la fase 2 con funzione di: Quando fila dal PersonAddress sta per essere cancellato di controllo se non ci sono più righe con lo stesso ID Indirizzo nella tabella PersonaIndipendente, nel qual caso eliminarlo dalla tabella Indirizzo.

Maggiori informazioni qui:
How to cascade delete over many to many table
How do I delete from multiple tables using INNER JOIN in SQL server

Problemi correlati