EF Core 3.1.7 Annotazioni dei dati per più relazioni 1:1 nella tabella
Ho problemi a capire le annotazioni dei dati per mappare più di una relazione 1:1 in modo che EF Core 3.11.7 lo capisca e possa creare una migrazione.
Ho una tabella Persona e una tabella Note. Esiste una relazione 0:M Notes in Person. Un record di persona può avere 0 o più note.
Nella tabella delle note c'è un campo CreatedBy che è una persona. Ha anche un campo LastEditedBy che è anche una persona. EF continua a bombardare su come costruire la relazione per Note.CreatedBy. Se questo non fosse EF, entrambi i campi sarebbero interi con il PersonID del record della persona corretta. In che modo, preferibilmente con le annotazioni dei dati, lo spiega a EF Core?
Quando provo a creare una migrazione fallisce e dice: System.InvalidOperationException: Impossibile determinare la relazione rappresentata dalla proprietà di navigazione 'Note.CreatedBy' di tipo 'Person'. Configurare manualmente la relazione o ignorare questa proprietà utilizzando l'attributo '[NotMapped]' o utilizzando 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Threading.Tasks;
namespace VetReg.Domain.Model
{
public class Family
{
public int FamilyID { get; set; } = -1;
public string FamilyName { get; set; }
public List<Pet> Pets { get; set; } = new List<Pet>();
public List<PersonFamily> People { get; set; }
public int AddressID { get; set; } = -1;
public List<Note> Notes { get; set; }
}
public class Person
{
public int PersonID { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public DateTime? Birthdate { get; set; }
public string Password { get; set; }
public List<PersonFamily> Families { get; set; }
public List<Note> Notes { get; set; }
} // class People
public class Note
{
public int NoteID { get; set; }
public int CreatedByID { get; set; }
[ForeignKey("CreatedByID")]
public Person CreatedBy { get; set; }
public DateTime DateCreated { get; set; }
public int LastEditByID { get; set; }
[ForeignKey("LastEditByID")]
public Person LastEditBy { get; set; }
public DateTime? LastEditDate { get; set; }
public string NoteText { get; set; }
}
public class PersonFamily
{
public int PersonID { get; set; }
public int FamilyID { get; set; }
public Person Person { get; set; }
public Family Family { get; set; }
}
}
Risposte
La domanda è (e questo è ciò che rende impossibile a EF determinare automaticamente le relazioni) qual è la relazione tra Person.Notes
e Note.CreatedBy
/ Note.LastEditBy
- nessuna probabilmente? Hai detto che c'è una relazione 0:M tra Person
e Note
, ma nota che ci sono potenzialmente 3 relazioni uno-a-molti lì - note associate a persona, note create da persona e note modificate da persona, che potenzialmente portano a 3 FK a Persona in nota.
Si noti inoltre che nessuna delle proprietà di navigazione è richiesta, ma quando presenti devono essere abbinate.
Supponendo che tu voglia 3 relazioni, cioè non c'è alcuna relazione tra Note.CreatedBy
/ Note.LastEditBy
e Person.Notes
, devi dire a EF che Note.CreatedBy
e Note.LastEditBy
non hai una proprietà di navigazione corrispondente (ovvero inversa) in Person
. Questo non è possibile con le annotazioni dei dati. L'unica annotazione di dati disponibile a tale scopo [InverseProperty(...)]
non accetta nomi di stringhe vuote/nulle, quindi non può essere utilizzata per ciò che è necessario qui.
Inoltre c'è un altro problema qui che incontrerai dopo aver risolto la corrente, che non può essere risolto con le annotazioni dei dati. Poiché hai più relazioni richieste (quindi l'eliminazione a cascata per impostazione predefinita) da Person
a Note
, crea il famoso problema "cicli o più percorsi a cascata" con SqlServer e richiede la disattivazione di almeno una delle eliminazioni a cascata.
Detto questo, il modello in questione necessita della seguente configurazione fluente minima:
modelBuilder.Entity<Note>()
.HasOne(e => e.CreatedBy)
.WithMany()
.OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<Note>()
.HasOne(e => e.LastEditBy)
.WithMany()
.OnDelete(DeleteBehavior.Restrict);
L'essenziale per l'emissione originale sono le coppie HasOne
/ . WithMany
Dopo averlo fatto, EF Core eseguirà automaticamente il mapping della Person.Notes
raccolta non mappata a una terza relazione facoltativa senza proprietà di navigazione inversa e proprietà shadow FP (e colonna) denominata "PersonId", ovvero l'equivalente di
modelBuilder.Entity<Person>()
.HasMany(e => e.Notes)
.WithOne()
.HasForeignKey("PersonId");
Per quanto riguarda il secondo problema con più percorsi a cascata, invece di Restrict
utilizzare qualsiasi opzione non a cascata o il più recente ClientCascade
. E potrebbe essere solo per una delle relazioni, non appena interrompe il "percorso a cascata" (apparentemente non puoi interrompere il ciclo perché è richiesto dal modello).