Sto tentando di fare quanto segue:
public class class1
{
public int Id {get;set;}
[ForeignKey("Class2")]
public int Class2Id {get;set;}
public virtual Class2 Class2 {get;set;}
}
public class class2
{
public int Id { get; set;}
[Required]
public virtual int Class1Id {get;set;}
[Required]
[ForeignKey("Class1Id")]
public Class1 Class1 {get;set;}
}
Tuttavia ogni volta che provo a migrare il mio database ottengo il seguente errore:
Class1_Class2_Target:: Multiplicity non è valido nel ruolo 'Class2_Class1_Target' nella relazione 'Class2_Class1'. Poiché le proprietà del ruolo dipendente non sono le proprietà chiave, il limite superiore della molteplicità del ruolo dipendente deve essere '*'.
Quale potrebbe essere il problema qui?
Il tuo modello non è un'associazione 1: 1. È ancora possibile avere molte Class2
oggetti che si riferiscono alla stessa Class1
oggetto. Inoltre, il tuo modello non garantisce che una Class2
fa riferimento a una Class1
venga rinviata anche da questo oggetto di Class1
- La Class1
può riferirsi a qualsiasi oggetto Class2
.
Il modo comune per garantire (una sorta) un'associazione 1: 1 in SQL è avere una tabella per l'entità principale e una per l'entità dipendente dove la chiave primaria nella tabella dipendente è anche una chiave esterna per l'entità:
(Qui Class1
è il principale)
Ora in un database relazionale, questo non garantisce ancora un'associazione 1: 1 (ecco perché ho detto "sorta di"). È un'associazione 1: 0..1 . Ci può essere un Class1
senza un Class2
. La verità è che le autentiche associazioni 1: 1 sono impossibili in SQL, perché non esiste un costrutto linguistico che inserisca due righe in tabelle diverse in modo sincrono. 1: 0..1 è il più vicino che otteniamo.
Mappatura fluente
Per modellare questa associazione in EF è possibile utilizzare l'API fluente. Ecco il modo standard per farlo:
class Class1Map : EntityTypeConfiguration<Class1>
{
public Class1Map()
{
this.HasKey(c => c.Id);
this.Property(c => c.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
this.HasRequired(c1 => c1.Class2).WithRequiredPrincipal(c2 => c2.Class1);
}
}
E nel contesto:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new Class1Map());
}
E questo è rimasto delle tue classi:
public class Class1
{
public int Id {get;set;}
public virtual Class2 Class2 {get;set;}
}
public class Class2
{
public int Id {get;set;}
public virtual Class1 Class1 {get;set;}
}
Non è possibile configurare le proprietà di chiavi esterne alternative nel modello, poiché l'unico FK coinvolto deve essere la chiave primaria del dipendente.
La cosa strana di questo modello è che EF non ti impedisce di creare (e salvare) un oggetto class1
senza class2
. Penso che EF dovrebbe essere in grado di convalidare questo requisito prima di salvare le modifiche, ma, a quanto pare, non lo è. Allo stesso modo, ci sono modi per eliminare un oggetto class2
senza eliminare il suo genitore di class1
. Quindi questa coppia HasRequired
- WithRequired
non è così rigida come sembra (e dovrebbe essere).
Annotazioni di dati
L'unico modo per ottenere questo diritto nel codice è da annotazioni di dati. (Ovviamente il modello di database non sarà ancora in grado di applicare 1: 1)
public class Class1
{
public int Id {get;set;}
[Required]
public virtual Class2 Class2 {get;set;}
}
public class Class2
{
[Key, ForeignKey("Class1")]
public int Id {get;set;}
[Required]
public virtual Class1 Class1 {get;set;}
}
L'annotazione [Key, ForeignKey("Class1")]
dice a EF che Class1
è l'entità principale.
Le annotazioni dei dati hanno un ruolo in molte API, che possono essere una maledizione, perché ogni API sceglie il proprio sottoinsieme da implementare, ma qui è utile, perché ora EF non le utilizza solo per progettare il modello di dati, ma anche per convalidare le entità . Ora se provi a salvare un oggetto class1
senza un class2
otterrai un errore di convalida.