21

EF4 CTP 5、CodeFirst を使用しています。

最初に私のクラスを見てください:

public class Guest
{
        [Key]
        public Guid GuestID { get; set; }

        public Language PreferredLanguage { get; set; }
        public Guid? LanguageID { get; set; }
}

public class Language
{
        [Key]
        public Guid LanguageID { get; set; }

        [Required(ErrorMessage = "Enter language name")]
        [StringLength(50, ErrorMessage = "Language name is too long")]
        public string LanguageName { get; set; } // in origine language
}

私の目標は、Guest-Language 関係に特定の「削除ルール」を設定することです。言語が削除されたときに、対応するゲストを削除したくありません(カスケード削除はありません)。代わりに、ゲストの LanguageID を「Set NULL」にする必要があります。

流暢な API がここで私をサポートしてくれることを期待していました。しかし、必要なオプションを提供しない .WillCascadeOnDelete(bool) 以外に役立つものは見つかりませんでした。何か見逃しましたか?それとも、これは CTP 5 に実装されていないだけですか?

助けてくれてありがとう!

4

2 に答える 2

37

探しているものは、とエンティティ間のオプションの関連付けを設定することで実現できます。GuestLanguage

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Guest>()
                .HasOptional(p => p.PreferredLanguage)
                .WithMany()
                .HasForeignKey(p => p.LanguageID);
}

単体テスト:

using (var context = new Context())
{
    var language = new Language()
    {
        LanguageName = "en"
    };
    var guest = new Guest()
    {
        PreferredLanguage = language
    };
    context.Guests.Add(guest);
    context.SaveChanges();

    context.Languages.Remove(language);
    context.SaveChanges();
}

その結果、guestLanguageID FK 列の DB null 値を持つレコードが作成されます。


アップデート:

まず、SQL プロファイラーを調べて、上記の単体テストが成功した理由を見てみましょう。以下は、2 番目の SaveChanges() メソッドを呼び出した直後のトレースを示しています。

ここに画像の説明を入力 ここに画像の説明を入力

ご覧のとおり、EF は十分に賢く、最初にゲスト レコードLanguageIDを null に設定して更新し、delete ステートメントを送信して言語レコードを削除します。これは、オプションの関連付けを設定するときの既定の EF の動作です。したがって、EFによってアプリケーション側で処理されています。もちろん、SQL Server内の言語レコードを手動で削除しようとすると、DBMSからエラーが発生します。

ただし、この話には続きがあります。次の単体テストを検討してください。

using (var context = new Context())
{
    var language = new Language() { LanguageName = "en" };
    var guest = new Guest() { PreferredLanguage = language };
    context.Guests.Add(guest);
    context.SaveChanges();
}

using (var context = new Context())
{
    var language = context.Languages.First();        
    context.Languages.Remove(language);
    context.SaveChanges();
}     

これは、レコードを手動で削除しようとしたときに SQL Server から取得した正確なメッセージを含む SQLException をスローして失敗します。その理由は、2 番目の単体テストでは関連するゲスト オブジェクトがコンテキストに読み込まれていないため、EF はそれを認識せず、最初の例のように必要な更新ステートメントを送信しないためです。

ご質問に戻りますが、残念ながら EF Code First ではリレーションシップの削除/更新ルールを明示的に変更することはできませんが、この投稿で例を示しているように、いつでもSqlCommandメソッドに頼ることができます。あなたの場合、次のようにコーディングできます。

protected override void Seed(Context context)
{
    context.Database.SqlCommand("ALTER TABLE dbo.Guests DROP CONSTRAINT Guest_PreferredLanguage");
    context.Database.SqlCommand("ALTER TABLE dbo.Guests ADD CONSTRAINT Guest_PreferredLanguage FOREIGN KEY (LanguageID) REFERENCES dbo.Languages(LanguageID) ON UPDATE NO ACTION ON DELETE SET NULL");
}

これはあなたが探しているものです。上記のシード メソッドを配置すると、2 番目の単体テストもパスします。

これが役に立てば幸いです、
モルテザ

于 2011-02-19T14:57:06.803 に答える
0

Entity Framework Core で、こんなことをした...

modelBuilder.Entity<Guest>()
   .HasOne(g => g.Language)
   .WithMany(l => l.Guests) // I needed a return collection
   .OnDelete(DeleteBehavior.SetNull);
于 2018-02-01T23:17:54.000 に答える