EF Core 3.1.7 Annotazioni dei dati per più relazioni 1:1 nella tabella

Aug 17 2020

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

IvanStoev Aug 17 2020 at 15:11

La domanda è (e questo è ciò che rende impossibile a EF determinare automaticamente le relazioni) qual è la relazione tra Person.Notese Note.CreatedBy/ Note.LastEditBy- nessuna probabilmente? Hai detto che c'è una relazione 0:M tra Persone 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.LastEditBye Person.Notes, devi dire a EF che Note.CreatedBye Note.LastEditBynon 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 Persona 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/ . WithManyDopo averlo fatto, EF Core eseguirà automaticamente il mapping della Person.Notesraccolta 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 Restrictutilizzare 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).