Ausgabe
Ich stecke in einem Problem fest, das ich versuche, auf die richtige Weise zu lösen.
Ich habe mehrere Eins-zu-viele-Eigentumsbeziehungen. Aber ich habe eine doppelte Beziehung zwischen zwei Kindern, A sollte eine Liste von Es haben, und das verkettete Kind D sollte auf ein D aus dieser Liste zeigen. Ich kann nicht festlegen, dass eine Klasse 2 Entitäten gehört, und wenn ich nur eine Beziehung habe, kann ich nicht die gesamte Struktur auf einmal speichern. Beispiel dafür, wie meine Codestruktur aussieht.
public class A //Main structure class
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id {get; set;}
[ForeignKey("FK_A_ID")]
public List<B> Bs {get; set;} //Set as OwnsMany
[ForeignKey("FK_A_ID")]
public List<D> Ds {get; set;} //Set as OwnsMany
}
public class B
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id {get; set;}
[ForeignKey("FK_B_ID")]
public List<C> Cs {get; set;} //Set as OwnsMany
public int FK_A_ID { get; set; }
}
public class C
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id {get; set;}
[ForeignKey("FK_C_ID")]
public List<D> Ds {get; set;} //Set as OwnsMany
public int FK_B_ID { get; set; }
}
public class D //referencing to one
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id {get; set;}
[ForeignKey("FK_D_ID")]
public List<E> Fs {get; set;} //Set as OwnsMany
public int FK_C_ID { get; set; }
}
// Child that I need to have in both one A (A OwnsMany E) and multiple Ds (D has/owns one E respectively E has/owns multiple Ds)
public class E
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id {get; set;}
[ForeignKey("FK_E_ID")]
public List<F> Fs {get; set;} // Trying to set as OwnsMany/HasMany
public int FK_A_ID { get; set; }
}
Das Problem ist, dass ich nicht weiß, wie ich diese Beziehung am besten für EF Core richtig angeben kann.
Ich habe versucht, die Beziehung AE zu besitzen und die Beziehung DE oder ED zu haben, die einen Fehler bei der Migration auslöst. Mit der Beziehung AE löst das Speichern eine Fremdschlüsselverletzung aus, da FK 0 ist, da A noch nicht gespeichert wurde und keine ID hat. Ich möchte alles als eins speichern (wenn möglich) und alles als eins laden, da ich das Angular-Frontend habe, das es erwartet und bearbeitet zurücksendet.
Ich hoffe, jemand hat ein solches Szenario bereits gelöst, aber ich kann in der Dokumentation nichts für einen solchen Fall finden.
Lösung
Grundsätzlich handelt OwnsMany
es sich um eine spezielle Art von Beziehung, die nicht geeignet ist, sobald Sie mehrere Entitäten mit Navigationseigenschaften zum gleichen Ziel haben. In solchen Fällen sollten Sie HasMany
Beziehung verwenden.
Wenn Sie mehrere Verweise auf dieselbe Entität haben (wie TransitionOptions
das zu einigen gehört CurrentStep
und auf einige verweist NextStep
), verwenden Sie FluentApi oder kommentieren Sie mit InversePropertyAnnotation
, nicht mit ForeignKeyAnnotation
.
Im Allgemeinen würde ich Ihnen raten, eine fließendere API-Konfiguration und eine weniger auf Anmerkungen basierende Konfiguration zu verwenden – sie ist einfacher zu warten.
Hier ist ein Beispielcode, der auf Ihrer Frage basiert.
Modellklassen
public class Story
{
public int Id { get; set; }
public List<Step>? Steps { get; set; }
public List<Variable>? Variables { get; set; }
}
public class Variable
{
public int Id { get; set; }
public int StoryId { get; set; }
public string? Value { get; set; }
public Story? Story { get; set; }
public List<Change>? Changes { get; set; }
}
public class Step
{
public int Id { get; set; }
public int StoryId { get; set; }
public Story? Story { get; set; }
public List<TransitionOption>? TransitionOptions { get; set; }
}
public class TransitionOption
{
public int Id { get; set; }
public int StepId { get; set; }
public int? NextStepId { get; set; }
public Step? CurrentStep { get; set; }
public Step? NextStep { get; set; }
public List<Change>? Changes { get; set; }
}
public class Change
{
// If combination of TransitionOptionId and VariableId is unique,
// you can use them as composite key instead
public int Id { get; set; }
public int TransitionOptionId { get; set; }
public int VariableId { get; set; }
public Variable? Variable { get; set; }
public TransitionOption? TransitionOption { get; set; }
}
Entitätskonfigurationsklassen
internal class StoryConfiguration : IEntityTypeConfiguration<Story>
{
public void Configure(EntityTypeBuilder<Story> builder)
{
builder.HasMany(x => x.Variables)
.WithOne(x => x.Story)
.HasForeignKey(x => x.StoryId)
.OnDelete(DeleteBehavior.Cascade);
builder.HasMany(x => x.Steps)
.WithOne(x => x.Story)
.HasForeignKey(x => x.StoryId)
.OnDelete(DeleteBehavior.Cascade);
}
}
internal class VariableConfiguration : IEntityTypeConfiguration<Variable>
{
public void Configure(EntityTypeBuilder<Variable> builder)
{
builder.HasMany(x => x.Changes)
.WithOne(x => x.Variable)
.HasForeignKey(x => x.VariableId)
.OnDelete(DeleteBehavior.Cascade);
}
}
internal class StepConfiguration : IEntityTypeConfiguration<Step>
{
public void Configure(EntityTypeBuilder<Step> builder)
{
builder.HasMany(x => x.TransitionOptions)
.WithOne(x => x.CurrentStep)
.HasForeignKey(x => x.StepId)
.OnDelete(DeleteBehavior.Cascade);
// No navigation property in Step for TransitionOptions pointing
// to this as NextStep.
// It would also be possible to define such a property and mention it
// in HasMany
builder.HasMany<TransitionOption>()
.WithOne(x => x.NextStep)
.HasForeignKey(x => x.NextStepId)
// If next step is deleted, only disconnect referencing TransitionOptions
.OnDelete(DeleteBehavior.SetNull);
}
}
internal class TransitionOptionConfiguration : IEntityTypeConfiguration<TransitionOption>
{
public void Configure(EntityTypeBuilder<TransitionOption> builder)
{
builder.HasMany(x => x.Changes)
.WithOne(x => x.TransitionOption)
.HasForeignKey(x => x.TransitionOptionId)
.OnDelete(DeleteBehavior.Cascade);
}
}
Wenden Sie die Konfigurationen innerhalb der OnModelCreating
Methode an.
Beispielmethode zum Erstellen einer ganzen Story-Struktur mit 1SaveChanges
public async Task InitDb(MyDatabase db)
{
var variable1 = new Variable
{
Value = "1st Value"
};
db.Stories?.Add(new Story
{
Variables = new List<Variable>
{
variable1
},
Steps = new List<Step>
{
new Step
{
TransitionOptions = new List<TransitionOption>
{
new TransitionOption
{
Changes = new List<Change>
{
new Change
{
Variable=variable1
}
}
}
}
}
}
});
await db.SaveChangesAsync();
}
Ich hoffe, dieses Beispiel zeigt alles, was Sie für Ihre beschriebene Struktur benötigen.
Beantwortet von – grek40
Antwort geprüft von – Katrina (FixError Volunteer)