【问题标题】:How to solve combined one-to-one and one-to-many relationship in EF 5 Code First如何在EF 5 Code First中解决组合的一对一和一对多关系
【发布时间】:2013-02-07 13:09:48
【问题描述】:

我正在使用 Entity Framework 5 和 Code First。

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

这是实体的代码:

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:我应该怎么做才能在到数据库的一次往返中插入 Question 对象和引用的 Answers(通过属性 PossibleAnswers)(例如,一次调用上下文 SaveChanges)?当我在不先添加答案的情况下保存问题和答案时出现的错误是:

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

为了解决这个问题,我尝试了以下方法,使用 fluent API 在问题之前添加答案,只需调用 objectcontexts SaveChanges 即可:

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

        base.OnModelCreating(modelBuilder);
    }

但是,这导致我犯了另一个错误:

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

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

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

【问题讨论】:

    标签: c# ef-code-first entity-framework-5 .net-4.5


    【解决方案1】:

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

    Q1:第一个调用将Question.CorrectAnswer 设置为null,第二个调用将CorrectAnswer 设置为一个存储的答案。

    Q2:第一次调用将Question.CorrectAnswer设置为null,第二次调用删除Question和启用级联删除的相关答案。

    如果您不太担心两次往返,而是更担心与两个 SaveChanges 调用对应的两个事务,您可以将包括两个 SaveChanges 调用在内的整个操作包装到一个手动事务中。 (例如:EF: How do I call SaveChanges twice inside a transaction?

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

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

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

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

    【讨论】:

    • 我仍然没有让它工作,但我会阅读你建议的链接。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-09-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多