EF Core 3.1.7 Annotations de données pour plusieurs relations 1:1 dans le tableau
J'ai des problèmes pour comprendre les annotations de données pour mapper plus d'une relation 1:1 afin qu'EF Core 3.11.7 le comprenne et puisse créer une migration.
J'ai une table Person et une table Notes. Il existe une relation 0:M Notes dans Person. Un enregistrement de personne peut avoir 0 ou plusieurs notes.
Dans la table des notes se trouve un champ CreatedBy qui est une personne. Il a également un champ LastEditedBy qui est également une personne. EF continue de bombarder sur la façon de construire la relation pour Note.CreatedBy. Si ce n'était pas EF, les deux champs seraient des entiers avec le PersonID de l'enregistrement de personne approprié. Comment faire, de préférence avec des annotations de données, expliquer cela à EF Core ?
Lorsque j'essaie de créer une migration, elle échoue et indique : System.InvalidOperationException : Impossible de déterminer la relation représentée par la propriété de navigation "Note.CreatedBy" de type "Person". Configurez manuellement la relation ou ignorez cette propriété en utilisant l'attribut '[NotMapped]' ou en utilisant 'EntityTypeBuilder.Ignore' dans '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; }
}
}
Réponses
La question est (et c'est ce qui rend impossible à EF de déterminer automatiquement les relations) quelle est la relation entre Person.Notes
et Note.CreatedBy
/ Note.LastEditBy
- aucune probablement ? Vous avez dit qu'il existe une relation 0: M entre Person
et Note
, mais notez qu'il existe potentiellement 3 relations un à plusieurs - des notes associées à une personne, des notes créées par une personne et des notes modifiées par une personne, ce qui conduit potentiellement à 3 FK à Personne en note.
Notez également qu'aucune des propriétés de navigation n'est requise, mais lorsqu'elles sont présentes, elles doivent être associées.
En supposant que vous vouliez 3 relations, c'est-à-dire qu'il n'y a pas de relation entre Note.CreatedBy
/ Note.LastEditBy
et Person.Notes
, vous devez le dire à EF Note.CreatedBy
et Note.LastEditBy
ne pas avoir la propriété de navigation correspondante (alias inverse) dans Person
. Ceci n'est pas possible avec les annotations de données. La seule annotation de données disponible à cette fin [InverseProperty(...)]
n'accepte pas de nom de chaîne vide/null, et ne peut donc pas être utilisée pour ce qui est nécessaire ici.
Il y a aussi un autre problème ici que vous rencontrerez après avoir résolu le courant, qui ne peut pas non plus être résolu avec des annotations de données. Étant donné que vous avez plusieurs relations requises (donc suppression en cascade par défaut) de Person
à Note
, cela crée le fameux problème "cycles ou plusieurs chemins en cascade" avec SqlServer, et nécessite de désactiver au moins une des suppressions en cascade.
Cela étant dit, le modèle en question nécessite la configuration fluide minimale suivante :
modelBuilder.Entity<Note>()
.HasOne(e => e.CreatedBy)
.WithMany()
.OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<Note>()
.HasOne(e => e.LastEditBy)
.WithMany()
.OnDelete(DeleteBehavior.Restrict);
L'essentiel pour le numéro d'origine sont les paires HasOne
/ . WithMany
Une fois que vous faites cela, EF Core mappera automatiquement la Person.Notes
collection non mappée à une troisième relation facultative sans propriété de navigation inverse et propriété FP fantôme (et colonne) appelée "PersonId", c'est-à-dire l'équivalent de
modelBuilder.Entity<Person>()
.HasMany(e => e.Notes)
.WithOne()
.HasForeignKey("PersonId");
En ce qui concerne le deuxième problème avec plusieurs chemins en cascade, Restrict
vous pouvez utiliser n'importe quelle option non en cascade ou le plus récent ClientCascade
. Et cela pourrait être pour une seule des relations, dès qu'elle rompt le "chemin en cascade" (apparemment, vous ne pouvez pas rompre le cycle car il est exigé par le modèle).