3

Entity Framework 5 と Code First を使用しています。

クイズ アプリケーションの2 つのドメイン エンティティQuestionAnswerがあります。1 つの質問には、複数の可能な回答があります。質問には、可能な回答の 1 つを参照する必要がある 1 つの正解もあります。to エンティティ間の 1 対多および 1 対 1 の関係の組み合わせでいくつかの問題が発生しています。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:データベースへの 1 回のラウンドトリップ (たとえば、コンテキスト SaveChanges への 1 回の呼び出し) で Question オブジェクトと参照された Answers (PossibleAnswers プロパティを介して) を挿入できるようにするにはどうすればよいですか? 最初に回答を追加せずに質問と回答を保存すると、次のエラーが表示されます。

依存操作の有効な順序を判別できません。外部キーの制約、モデルの要件、またはストアで生成された値が原因で、依存関係が存在する場合があります。

その問題を解決するために、流暢なAPIを使用して次のことを試し、オブジェクトコンテキストのSaveChangesを1回呼び出すだけですべてを実行するときに、質問の前に回答が追加されるようにしました。

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

        base.OnModelCreating(modelBuilder);
    }

ただし、それにより別のエラーが発生しました。

競合する変更が検出されました。これは、同じキーを持つ複数のエンティティを挿入しようとしたときに発生することがあります。

Q1 の流暢な API アプローチで正しい道を進んでいますか? なぜエラーメッセージが表示されるのですか?

Q2 : 質問を削除すると、回答の前に質問を削除することはできず、またその逆もできないため、エラーが発生することがわかりました。これを解決するにはどうすればよいですか?たとえば、WillCascadeOnDelete は Question.CorrectAnswer と Question.PossibleAnswers の両方で指定する必要がありますか?

4

1 に答える 1

2

あなたの質問Q1とQ2の両方について、2回の往復/ 2回の呼び出しが必要になりますSaveChanges(おそらくストアドプロシージャで問題を解決することは別として)。

Q1: に設定された最初の呼び出しとQuestion.CorrectAnswer、保存されている回答の 1 つにnullを設定する 2 番目の呼び出し。CorrectAnswer

Q2: への最初の呼び出し設定Question.CorrectAnswernull2 番目の呼び出しにより、Questionカスケード削除が有効になっている関連する回答が削除されます。

2 回のラウンドトリップについてはあまり気にせず、2 つの呼び出しに対応する 2 つのトランザクションについて心配している場合は、2 つのSaveChanges呼び出しを含む操作全体をSaveChanges1 つの手動トランザクションにラップできます。(例: EF: トランザクション内で SaveChanges を 2 回呼び出すにはどうすればよいですか? )

1 対 1 の関係について: ビジネスの観点からは の関係CorrectAnswerは 1 対 1 ですが、EF との 1 対 1 の関係としてモデル化することは困難または不可能です。

問題は、EF が外部キーの 1 対 1 の関連付け、つまり外部キー (CorrectAnswerId など) に一意の制約がある関係をサポートしていないことです。従属 ( ) の主キーが、同時にプリンシパル ( )Questionに対する外部キー ( の ) である、共有主キーの 1 対 1 の関連付けのみをサポートします。Fluent コードは、このような共有主キーの関連付けの構成です。ただし、有効なのはと同じ主キー値を持つのみであることを意味します。これは理論的には可能ですが (テーブルにはテーブルよりも多くのレコードがあります)、自動生成されたキーを使用せずにキーを手動で提供する必要がある可能性が高くなります。を一から変えるQuestion.CorrectAnswerAnswerCorrectAnswerAnswerQuestionAnswerQuestionCorrectAnswersAnswer他には不可能でしょう。したがって、私の意見では、共有主キーはあなたのモデルには適していません。

より良い解決策は、Fluent マッピングを削除することです。CorrectAnswer結果は、テーブル内のnull 許容外部キーとの 1 対多の関係になりQuestionます。データベースの観点からは、すべての質問には独自の一意の回答セットがあり、2 つの質問が同じ回答を共有することはないため、ビジネス ロジックではおそらくナンセンスである多くの s に対して同じAnswerことができることを意味します。ただし、逆コレクション プロパティ ( など) を に追加しないだけで、この 1 対多の関係をビジネス ロジックから「隠す」ことができます。ビジネス上の制約を完全にモデル化するわけではありませんが、技術的には問題なく動作します。CorrectAnswerQuestionQuestionsThisIsTheCorrectAnswerForAnswer

EF との 1 対 1 の関係の難しさの詳細については、次のブログ投稿を参照してください。

于 2013-02-07T23:15:32.607 に答える