Entity Framework 核心 - owned child 和另一个 chained owned child 之间的关系
Entity Framework Core - Relationship between owned child and another chained owned child
我遇到了问题,我正在尝试以正确的方式解决。
我有多个 one-to-many 'owning' 关系。但是我在两个 children Story 之间有一个重复的关系应该有一个 Variables 的列表并且链接 child Change应该指向此列表中的一个 Variable。我无法将一个 class 设置为由 2 个实体拥有,并且只有 'having' 关系我无法立即保存整个结构。我的代码结构的示例。
编辑:另一个问题来自 TransitionOption,它有 Step 它所属的 Step[=44] =] 它指向的地方。
public class Story //Main structure example class - A before Edit
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
...name, creator...
[ForeignKey("FK_Story_ID")]
public List<Step> Steps { get; set; } //Set as OwnsMany
[ForeignKey("FK_Story_ID")]
public List<Variable> Variables { get; set; } //Set as OwnsMany , Full list of Variables, that will be shown, which can but does not need to be changed during Step transitions
}
//example class with some main text about changes like part of the story
public class Step
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
...some text...
[ForeignKey("FK_Step_ID")]
public List<TransitionOption> TransitionOptions { get; set; } //Set as OwnsMany
public int FK_Story_ID { get; set; }
}
//example class for some option pointing to another Step
public class TransitionOption
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[ForeignKey("FK_TransitionOption_ID")]
public List<Change> Changes { get; set; } //Set as OwnsMany
public int FK_Step_ID { get; set; }
public int FK_NextStep_ID { get; set; } //this will also cause problem and I forgot to mention it before Edit
}
//referencing to one Variable from List Variables in Story
public class Change
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[ForeignKey("FK_Change_ID")]
public Variable ChangingVariable { get; set; } //Set as OwnsOne/HasOne - or another way around with List of Changes in Variable. But this is more suitable for my case
public int FK_TransitionOption_ID { get; set; }
}
//Child that I need to have in both one Story (Story OwnsMany Variables) and multiple Changes (Change has/owns one Variable respectively Variable has/owns multiple Changes)
public class Variable
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
...Starting value, conditions, type (text/number)...
public int FK_Change_ID { get; set; }
}
状态将在最后的过程中保留,所以我不需要在数据库中跟踪它们。这更像是所有转换和变量的模板和容器。这个例子展示了一个类似互动故事的东西。有点符合我的人际关系。
问题 是,我不知道如何最好地为 EF Core 正确指定此关系。
编辑:另一个 Problem 随 TransitionOption 一起出现,它有 Step 所属的 Step 和下一个 Step 它指向的。我在编辑更易读的示例格式时发现了这个问题。
我正在尝试拥有关系 Story-Variable 和拥有关系 Change-Variable 或 Variable-Change,这会在迁移时引发错误。与有关系Story-Variable,保存抛出外键违规,因为FK是0,因为故事还没有保存,没有ID,此时0不知何故被当作有效身份证件。
我想将它们全部保存为一个(如果可能的话)并将它们全部作为一个加载,因为我有 Angular 前端期待它并将其发回编辑。
如果有遗漏或不易理解的地方,我会尽力更好地回答和编辑。感谢所有评论。
我希望有人已经解决了这种情况,但我在文档中找不到这种情况的任何内容。请帮忙。
编辑:我已将结构更改为更像是我提供给@grek40 的示例(谢谢)。我无法发布真正的 classes,但这个例子应该几乎 1:1 到我遇到的问题。
基本上,OwnsMany
是一种特殊的关系,一旦您拥有多个具有指向同一目标的导航属性的实体,这种关系就不适用了。在这种情况下,您应该使用 HasMany
关系。
当您对同一实体有多个引用时(例如 TransitionOptions
属于某些 CurrentStep
并指向某些 NextStep
),请使用 FluentApi 或使用 [=18= 注释], 而不是 ForeignKeyAnnotation
.
一般来说,我建议您使用更流畅的 api 配置和更少的基于注释的配置 - 这样更容易维护。
根据您的问题,这是一些示例代码。
型号类
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; }
}
实体配置类
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);
}
}
应用 OnModelCreating
方法中的配置。
用 1 创建整个故事结构的示例方法 SaveChanges
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();
}
希望这个例子展示了您描述的结构所需的一切。
我遇到了问题,我正在尝试以正确的方式解决。
我有多个 one-to-many 'owning' 关系。但是我在两个 children Story 之间有一个重复的关系应该有一个 Variables 的列表并且链接 child Change应该指向此列表中的一个 Variable。我无法将一个 class 设置为由 2 个实体拥有,并且只有 'having' 关系我无法立即保存整个结构。我的代码结构的示例。
编辑:另一个问题来自 TransitionOption,它有 Step 它所属的 Step[=44] =] 它指向的地方。
public class Story //Main structure example class - A before Edit
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
...name, creator...
[ForeignKey("FK_Story_ID")]
public List<Step> Steps { get; set; } //Set as OwnsMany
[ForeignKey("FK_Story_ID")]
public List<Variable> Variables { get; set; } //Set as OwnsMany , Full list of Variables, that will be shown, which can but does not need to be changed during Step transitions
}
//example class with some main text about changes like part of the story
public class Step
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
...some text...
[ForeignKey("FK_Step_ID")]
public List<TransitionOption> TransitionOptions { get; set; } //Set as OwnsMany
public int FK_Story_ID { get; set; }
}
//example class for some option pointing to another Step
public class TransitionOption
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[ForeignKey("FK_TransitionOption_ID")]
public List<Change> Changes { get; set; } //Set as OwnsMany
public int FK_Step_ID { get; set; }
public int FK_NextStep_ID { get; set; } //this will also cause problem and I forgot to mention it before Edit
}
//referencing to one Variable from List Variables in Story
public class Change
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[ForeignKey("FK_Change_ID")]
public Variable ChangingVariable { get; set; } //Set as OwnsOne/HasOne - or another way around with List of Changes in Variable. But this is more suitable for my case
public int FK_TransitionOption_ID { get; set; }
}
//Child that I need to have in both one Story (Story OwnsMany Variables) and multiple Changes (Change has/owns one Variable respectively Variable has/owns multiple Changes)
public class Variable
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
...Starting value, conditions, type (text/number)...
public int FK_Change_ID { get; set; }
}
状态将在最后的过程中保留,所以我不需要在数据库中跟踪它们。这更像是所有转换和变量的模板和容器。这个例子展示了一个类似互动故事的东西。有点符合我的人际关系。
问题 是,我不知道如何最好地为 EF Core 正确指定此关系。 编辑:另一个 Problem 随 TransitionOption 一起出现,它有 Step 所属的 Step 和下一个 Step 它指向的。我在编辑更易读的示例格式时发现了这个问题。
我正在尝试拥有关系 Story-Variable 和拥有关系 Change-Variable 或 Variable-Change,这会在迁移时引发错误。与有关系Story-Variable,保存抛出外键违规,因为FK是0,因为故事还没有保存,没有ID,此时0不知何故被当作有效身份证件。 我想将它们全部保存为一个(如果可能的话)并将它们全部作为一个加载,因为我有 Angular 前端期待它并将其发回编辑。
如果有遗漏或不易理解的地方,我会尽力更好地回答和编辑。感谢所有评论。
我希望有人已经解决了这种情况,但我在文档中找不到这种情况的任何内容。请帮忙。
编辑:我已将结构更改为更像是我提供给@grek40 的示例(谢谢)。我无法发布真正的 classes,但这个例子应该几乎 1:1 到我遇到的问题。
基本上,OwnsMany
是一种特殊的关系,一旦您拥有多个具有指向同一目标的导航属性的实体,这种关系就不适用了。在这种情况下,您应该使用 HasMany
关系。
当您对同一实体有多个引用时(例如 TransitionOptions
属于某些 CurrentStep
并指向某些 NextStep
),请使用 FluentApi 或使用 [=18= 注释], 而不是 ForeignKeyAnnotation
.
一般来说,我建议您使用更流畅的 api 配置和更少的基于注释的配置 - 这样更容易维护。
根据您的问题,这是一些示例代码。
型号类
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; }
}
实体配置类
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);
}
}
应用 OnModelCreating
方法中的配置。
用 1 创建整个故事结构的示例方法 SaveChanges
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();
}
希望这个例子展示了您描述的结构所需的一切。