.NET Core Entity Framework - 只添加新的子实体
.NET Core Entity Framework - Only add new child entities
我在更新具有数据库中已存在的子实体的父实体时遇到了一些问题。
我有一个与 Answer 实体存在多对多关系的 Question 实体。简化版本如下所示:
public class Question
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
[JsonIgnore]
public virtual List<QuestionParentRelation>? ParentQuestions { get; set; }
[JsonIgnore]
public virtual List<QuestionParentRelation>? ChildQuestions { get; set; }
public string Name { get; set; }
public virtual List<Answer>? PossibleAnswers { get; set; }
}
public class Answer
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public string AnswerText { get; set; }
[JsonIgnore]
public List<Question> Questions { get; set; }
}
但是与其他问题也存在多对多关系,因为在 UI 端有一个拖放功能,可以创建子后续问题等。
此关系包含 ParentQuestionAnswerId
(一个外键,指示在显示子问题之前应该对父问题给出哪个答案)。
public class QuestionParentRelation
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid RelationId { get; set; }
public Guid? ParentId { get; set; }
public Guid ChildId { get; set; }
public virtual Question ParentQuestion { get; set; }
public virtual Question ChildQuestion { get; set; }
public int ParentHashNum { get; set; }
public int ChildHashNum { get; set; }
// FK: The answer that is needed before the child question is shown
public Guid? ParentQuestionAnswerId { get; set; }
[JsonIgnore]
public virtual Answer? ParentQuestionAnswer { get; set; }
public int Level { get; set; }
public int Order { get; set; }
}
现在的问题是,每次我尝试更新问题实体(使用给定的现有答案实体列表)时:
- 我收到一条 错误消息,提示该条目存在重复项
具体答案 ID
- 如果我首先尝试从数据库中删除 Answer,我会收到一条错误消息,指出存在失败的外键约束 (ParentQuestionAnswerId)。
(但是如果首先删除 Answer 实体是唯一的解决方案,我不希望所有 QuestionParentRelation 实体都将 ParentQuestionAnswerId 设置为 NULL,因为那样会也会影响不应编辑的关系)
这是我在 FluentAPI 中配置的:
builder.Entity<Question>()
.HasMany(q => q.PossibleAnswers)
.WithMany(a => a.Questions)
.UsingEntity(join => join.ToTable("QuestionPossibleAnswers"));
builder.Entity<QuestionParentRelation>()
.ToTable("QuestionParentRelations")
.HasKey(r => new { r.RelationId });
builder.Entity<Question>()
.HasMany(q => q.ParentQuestions)
.WithOne(r => r.ChildQuestion)
.HasForeignKey(r => r.ChildId);
builder.Entity<Question>()
.HasMany(q => q.ChildQuestions)
.WithOne(r => r.ParentQuestion)
.HasForeignKey(r => r.ParentId);
好吧,对于任何偶然发现这个问题的人来说:我仍然不知道是否有一种方法可以让 EF 自行处理这个问题,但我通过循环问题的 DTO 中的答案解决了它对象,然后将它们与实体进行比较。
如果实体中已经存在答案,我只需将其从 DTO 中删除并将更新后的 DTO 发送到服务。这样,现有答案将不会被添加两次。
QuestionsController 的 HttpPut 方法内部
var question = _questionService.EagerGetBy(q => q.Id == Guid.Parse(id));
if (question is null)
return NotFound("Geen resultaat gevonden voor Id " + id);
if (questionDto.PossibleAnswers != null && question.PossibleAnswers != null)
{
questionDto.PossibleAnswers.ForEach(a =>
{
if (a.Id != null && question.PossibleAnswers.Contains(a))
questionDto.PossibleAnswers.Remove(a);
});
}
我在更新具有数据库中已存在的子实体的父实体时遇到了一些问题。
我有一个与 Answer 实体存在多对多关系的 Question 实体。简化版本如下所示:
public class Question
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
[JsonIgnore]
public virtual List<QuestionParentRelation>? ParentQuestions { get; set; }
[JsonIgnore]
public virtual List<QuestionParentRelation>? ChildQuestions { get; set; }
public string Name { get; set; }
public virtual List<Answer>? PossibleAnswers { get; set; }
}
public class Answer
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public string AnswerText { get; set; }
[JsonIgnore]
public List<Question> Questions { get; set; }
}
但是与其他问题也存在多对多关系,因为在 UI 端有一个拖放功能,可以创建子后续问题等。
此关系包含 ParentQuestionAnswerId
(一个外键,指示在显示子问题之前应该对父问题给出哪个答案)。
public class QuestionParentRelation
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid RelationId { get; set; }
public Guid? ParentId { get; set; }
public Guid ChildId { get; set; }
public virtual Question ParentQuestion { get; set; }
public virtual Question ChildQuestion { get; set; }
public int ParentHashNum { get; set; }
public int ChildHashNum { get; set; }
// FK: The answer that is needed before the child question is shown
public Guid? ParentQuestionAnswerId { get; set; }
[JsonIgnore]
public virtual Answer? ParentQuestionAnswer { get; set; }
public int Level { get; set; }
public int Order { get; set; }
}
现在的问题是,每次我尝试更新问题实体(使用给定的现有答案实体列表)时:
- 我收到一条 错误消息,提示该条目存在重复项 具体答案 ID
- 如果我首先尝试从数据库中删除 Answer,我会收到一条错误消息,指出存在失败的外键约束 (ParentQuestionAnswerId)。 (但是如果首先删除 Answer 实体是唯一的解决方案,我不希望所有 QuestionParentRelation 实体都将 ParentQuestionAnswerId 设置为 NULL,因为那样会也会影响不应编辑的关系)
这是我在 FluentAPI 中配置的:
builder.Entity<Question>()
.HasMany(q => q.PossibleAnswers)
.WithMany(a => a.Questions)
.UsingEntity(join => join.ToTable("QuestionPossibleAnswers"));
builder.Entity<QuestionParentRelation>()
.ToTable("QuestionParentRelations")
.HasKey(r => new { r.RelationId });
builder.Entity<Question>()
.HasMany(q => q.ParentQuestions)
.WithOne(r => r.ChildQuestion)
.HasForeignKey(r => r.ChildId);
builder.Entity<Question>()
.HasMany(q => q.ChildQuestions)
.WithOne(r => r.ParentQuestion)
.HasForeignKey(r => r.ParentId);
好吧,对于任何偶然发现这个问题的人来说:我仍然不知道是否有一种方法可以让 EF 自行处理这个问题,但我通过循环问题的 DTO 中的答案解决了它对象,然后将它们与实体进行比较。
如果实体中已经存在答案,我只需将其从 DTO 中删除并将更新后的 DTO 发送到服务。这样,现有答案将不会被添加两次。
QuestionsController 的 HttpPut 方法内部
var question = _questionService.EagerGetBy(q => q.Id == Guid.Parse(id));
if (question is null)
return NotFound("Geen resultaat gevonden voor Id " + id);
if (questionDto.PossibleAnswers != null && question.PossibleAnswers != null)
{
questionDto.PossibleAnswers.ForEach(a =>
{
if (a.Id != null && question.PossibleAnswers.Contains(a))
questionDto.PossibleAnswers.Remove(a);
});
}