如何首先解决EF 5代码中的一对一和一对多组合关系

本文关键字:一对一 一对多 组合 关系 解决 何首先 EF 代码 | 更新日期: 2023-09-27 17:58:04

我正在使用实体框架 5 和代码优先。

我有两个用于测验应用程序的实体问题和答案。一个问题有几个可能的答案。一个问题也有一个正确答案,应该引用一个可能的答案。我在实体之间的一对多和一对一关系的组合中遇到了一些问题。参见问题 1问题 2

这是实体的代码:

public class Question
{
    public virtual int Id { get; set; }
    [Required]
    public virtual string Text { get; set; }
    [InverseProperty("Question")] 
    public virtual ICollection<Answer> PossibleAnswers { get; set; }
    public virtual Answer CorrectAnswer { get; set; }        
    [DatabaseGenerated(DatabaseGeneratedOption.Computed)]
    public virtual DateTime? UpdateStamp { get; set; }
}
public class Answer
{
    public virtual int Id { get; set; }
    [Required]
    public virtual string Text { get; set; }
    [ForeignKey("QuestionId")]
    public virtual Question Question { get; set; }
    public virtual int QuestionId { get; set; }
}

Q1:我应该怎么做才能在一次往返数据库(例如,对上下文 SaveChanges 的一次调用(中插入问题对象和引用的答案(通过属性 PossibleAnswers(?当我保存问题和答案而不先添加答案时,我得到的错误是:

无法确定从属操作的有效顺序。由于外键约束、模型要求或存储生成的值,可能存在依赖项。

为了解决这个问题,我尝试了以下内容,使用流畅的 API 在只需调用对象上下文 SaveChanges 即可完成所有操作时,在问题之前添加答案:

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Question>()
            .HasOptional(q => q.CorrectAnswer)
            .WithRequired();
        base.OnModelCreating(modelBuilder);
    }

但是,这导致我遇到另一个错误:

检测到冲突的更改。尝试插入具有相同键的多个实体时,可能会发生这种情况。

我在第一季度使用流畅的 API 方法是否走在正确的道路上?为什么出现错误消息?

Q2:删除问题时,我意识到会出现错误,因为无法在答案之前删除问题,反之亦然。我该如何解决这个问题?例如,WillCascadeOnDelete 是否应该同时在 Question.CorrectAnswer 和 Question.PossibleAnswers 上指定?

如何首先解决EF 5代码中的一对一和一对多组合关系

对于您的问题 Q1 和 Q2,您将需要两次往返/两次调用SaveChanges(除了解决存储过程的问题(。

问题 1:第一个呼叫Question.CorrectAnswer设置为 null 第二个呼叫,将CorrectAnswer设置为存储的答案之一。

Q2:第一个呼叫设置Question.CorrectAnswer null,第二个呼叫删除Question和相关答案,并启用级联删除。

如果您不太担心两次往返,而是更担心对应于两个SaveChanges调用的两个事务,则可以将包括两个SaveChanges调用在内的整个操作包装到一个手动事务中。(示例:EF:如何在事务中调用 SaveChanges 两次?

关于一对一关系:尽管从业务角度来看,CorrectAnswer的关系是一对一的,但很难甚至不可能将其建模为与 EF 的一对一关系。

问题是 EF 不支持外键一对一关联,即外键(CorrectAnswerId 左右(具有唯一约束的关系。它只支持共享主键一对一关联,其中依赖(Question(的主键是同时与主键(Answer(的外键(用于Question.CorrectAnswer(。您的 Fluent 代码是此类共享主键关联的配置。但这意味着唯一有效的CorrectAnswer是与Question具有相同主键值的Answer。虽然这在理论上是可以实现的(您的Answer表比Question表有更多的记录(,但它很可能不需要使用自动生成的键,而是手动提供键。将CorrectAnswers从一个Answer更改为另一个是不可能的。因此,在我看来,共享主键不适合您的模型。

更好的解决方案是删除您的 Fluent 映射。结果将是与Question表中CorrectAnswer的可为空外键的一对多关系。从数据库的角度来看,这意味着相同的Answer可能是许多QuestionCorrectAnswer,这在您的业务逻辑中可能是无稽之谈,因为每个问题都有自己独特的答案集,并且两个问题永远不会共享相同的答案。但是,您可以通过不向Answer添加反向集合属性(如 QuestionsThisIsTheCorrectAnswerFor(来从业务逻辑中"隐藏"这种一对多关系。尽管它不能完美地对业务约束进行建模,但它在技术上可以毫无问题地工作。

有关与 EF 一对一关系的困难的更多信息,请参阅以下博客文章:

  • 共享主键一对一关联
  • 一对一外键关联