1

誰かが私に、以下の私のEF(4.3)コードの最初のコードが「古い」パスワードを取得する結果になる理由を説明できますか?

using (var context = new CableSenseInstanceConfiguratorContext())
{
    var user = context.Installers.Where(u => u.UserName == "admin").FirstOrDefault();
    Console.WriteLine(user.Password); // Outputs "oldpassword"

    // Change the details on a different context;
    using (var context2 = new CableSenseInstanceConfiguratorContext())
    {
        var installer = context2.Installers.Single(i => i.UserName == "admin");
        installer.Password = "changed"; 
        context2.SaveChanges();
    }

    var user2 = context.Installers.Where(u => u.UserName == "admin").FirstOrDefault();
    Console.WriteLine(user2.Password); // Outputs "oldpassword"
}

開始するパスワードは「oldpassword」です。そのため、別のコンテキスト(context2)内でパスワードを変更し、それをuser2に再度フェッチしています。両方の出力が「oldpassword」であることを確認できます。SQLのプロファイリングから、パスワードが変更されていることがわかります。また、user2に入力するコードがデータベースに送信されていることもわかりますがこれらの値を使用していないだけです。

EFにはエンティティのキ​​ャッシュと追跡の方法としてローカルコンテキストの概念があることを理解していますが、私が理解していることcontext.Installers.Where(..)から、はデータベースからの再フェッチを強制するcontext.Installers.Find()必要がありますが、はローカルコンテキストを調べる必要があります。インストーラーをどのように照会しても、ローカルキャッシュを使用しているようです。

編集

解決策を提供してくれた@Reinardに感謝します。私はドキュメントを誤解していました-私はここから読みました:

DbSetとIDbSetは常にデータベースに対してクエリを作成し、返されたエンティティがコンテキストにすでに存在する場合でも、データベースへのラウンドトリップを常に伴うことに注意してください。

それで、データベースに行くので、オブジェクトを再フェッチすると思いました。実際に起こったことは、それがデータベースに行き、オブジェクトをフェッチし、私がすでにそのオブジェクトを追跡していることを発見したことです(以前のロードのため)、そして私は古いオブジェクトに行き着きました-これは実際にはドキュメントが言っていることです!

context.Installers.Local.Clear()驚くべきことに、使用しても違いはありません。AsNoTracking().

4

1 に答える 1

5

どちらもWhere、またはFindデータベースから明示的に再フェッチしません。私の知る限り、Findは、コンテキストにエンティティが存在しない場合にのみ、データベースからエンティティを取得します。

再フェッチを明示的に強制するには、AsNoTracking()を使用します。

例えば

context.Installers.AsNoTracking().Where(u => u.UserName == "admin").FirstOrDefault();
于 2012-07-09T10:41:23.593 に答える