web-dev-qa-db-ja.com

Entity Frameworkキャッシュの無効化/無効化

EFキャッシュに関する質問はたくさんありますが、私の問題に対する解決策はまだ見つかりません。

まっすぐな質問は

Entity Framework 6キャッシュを完全に無効にするにはどうすればよいですか?または、データに何かが起こったためにキャッシュを忘れるようにプログラムでEFに伝えることはできますか?

バックグラウンド

まず、inheritedEF(エンティティを定義するためのモデル優先)とプレーンデータ(データを操作するための)の奇妙な混合物で作成されたアプリケーションがあります。私がしたことは、アプリケーションをリファクタリングして次のことをすることでした。

  • EF6 LINQを使用する単純なクエリ(エンティティのGetAll()など)を作成する
  • 必要に応じてDbContext.Database.Connectionを使用して、SQLの複雑なデータ操作を終了します
  • Spring.Webサポートを追加して、DIおよびトランザクションを有効にします(まだではありません)

現時点では、アプリケーションのメイン機能(巨大なデータセットで複雑なSQLクエリを実行)が以前と同じように機能するようにコードを再編成しましたが、ルックアップドメインエンティティの操作はを使用してよりスマートに行われますできるだけ多くのEntity Framework

最も多く....

私が継承したページの1つは、マルチチェックボックスページです。これは、理解を深めるために表示します。壊れた機能の開発をブロックするよりも、現在の問題を修正してコードをリファクタリングする方が安価であるため、以前の実装者の選択については説明しません。

これはページがどのように見えるかです

enter image description here

基本的に、Controllerメソッドは次のとおりです。

    [HttpPost]
    public ActionResult Index(string[] codice, string[] flagpf, string[] flagpg, string[] flagammbce, string[] flagammdiv, string[] flagammest,
        string[] flagintab, string[] flagfinanz, string[] flagita, string[] flagest, string pNew){
            Sottogruppo2015Manager.ActivateFlagFor("pf", flagpf);
            Sottogruppo2015Manager.ActivateFlagFor("pg", flagpg);
            Sottogruppo2015Manager.ActivateFlagFor("ammbce", flagammbce);
            Sottogruppo2015Manager.ActivateFlagFor("ammdiv", flagammdiv);
            Sottogruppo2015Manager.ActivateFlagFor("ammest", flagammest);
            Sottogruppo2015Manager.ActivateFlagFor("intab", flagintab);
            Sottogruppo2015Manager.ActivateFlagFor("finanz", flagfinanz);
            Sottogruppo2015Manager.ActivateFlagFor("ita", flagita);
            Sottogruppo2015Manager.ActivateFlagFor("est", flagest);

            return RedirectToAction("Index", new { pNew });
 }

string[]パラメーターは、テーブル内の列です。 ActivateFlagForメソッドは、2つのクエリを順番に実行します

UPDATE table SET --param1-- = 0;
UPDATE table SET --param1-- = 1 where id in (--param2--)

キャッシュが作動したとき

動作は次のとおりです。

  • 最初にLINQ selectを発行するページをロードします。チェックは列の1と0に一致します
  • 1つ以上のチェックを変更して送信する
  • コントローラーは、DBのチェックを更新するクエリを発行します
  • redirecting(!は新しいリクエストを意味します!)ページをリロードする前に、DBを確認し、変更が適用されます
  • 上記と同じLINQ selectを発行するページのリロード:oldチェックが表示されます

アプリケーションをリロードすると問題が解決するため、これはキャッシュの問題であると確信しています。アプリケーションの主な機能は完全にSQLベースであるため、ルックアップテーブルへの変更はメイン操作に反映され、これが正しい動作です。

EFキャッシングはパフォーマンスにとって優れた機能であることを理解していますが、私の場合は、少なくともアプリケーション全体をLINQ DMLに移行するまでは(おそらく不可能です)望んでいません。

DbContextの使用方法

もちろん、「DbContextをどのように使用していますか?」 「正しく処分しますか?」。

  • マネージャークラスにSpringトランザクションをまだ統合していません
  • データベースでactionsを実行する各オブジェクトは、I<Entity>Manager拡張BaseManagerです
  • DbContextはリクエストスコープのSpringオブジェクトです。私はすでに リクエストスコープのオブジェクトの破棄について尋ねました ですが、現在、回避策を実装していますが、悪い一方で、正しく破棄するリクエスト。

サンプルコード

public class ExampleManagerImpl : BaseManager, IExampleManager
{
    public void ActivateFlagFor(string aFlag, string[] aList)
    {
        string sql = "UPDATE table SET flag" + aFlag + " = 0";
        RunStatementV1(sql);

        if (aList != null && aList.Any())
        {
            sql = "UPDATE table SET flag" + aFlag + " = 1 WHERE id in (" + aList.ToCsvApex() + ")";
            RunStatementV1(sql);
        }
    }

    public IList<Models.Example> GetAll()
    {
        return DataContext.example.ToList(); //I don't dispose of the DataContext willingly
    }
}

そして

public abstract class BaseManager {

    public DbContext DataContext { get; set; } //Autowired

    protected void RunStatementV1(string aSqlStatement)
    {
        IDbConnection connection = DataContext.Database.Connection;
        if (connection.State == ConnectionState.Closed || connection.State == ConnectionState.Broken) connection.Open(); //Needed because sometimes I found the connection closed, even if I don't dispose of it
        using (IDbCommand command = connection.CreateCommand())
        {
            command.CommandText = aSqlStatement;
            command.ExecuteNonQuery();
        }

    }
}

私が試したもの

データ取得のためにEF6のキャッシュを完全に無視したい場合は、AsNoTracking()をクエリの最後に追加します(ToList()を呼び出すか、クエリを実行する他の操作を行う前に)。

MSDN AsNoTracking()

これを行うと、既存のデータのキャッシュをチェックしたり、データベース呼び出しの結果をキャッシュに追加したりしないことに注意してください。また、Entity Frameworkは、データベースから取得したエンティティへの変更を自動的に検出しません。エンティティを変更してデータベースに保存する場合は、SaveChanges()を呼び出す前に、変更したエンティティを添付する必要があります。

現在の方法は次のとおりです。

public IList<Models.Example> GetAll()
{
    return DataContext.example.ToList();
}

次のように変更されます。

public IList<Models.Example> GetAll()
{
    return DataContext.example.AsNoTracking().ToList();
}

EFのキャッシュを処理する他のオプションに興味がある場合は、 EF6キャッシュバスティングに関するブログ投稿 を作成しました。

35
CodeThug

私もこの問題を抱えていましたが、修正できました。

リポジトリパターンを使用し、.Net CoreのデフォルトDIを使用しています。 AddSingleton(...)を使用していますが、DbContextで使用するのは間違っています。

そこで、ドキュメントから読むように、AddScopedに変更しました:スコープ付きライフタイムサービスは、リクエストごとに1回作成されます。

それは私の問題を解決しました。

ms docsからこのセクションを読む必要があります:Service Lifetimes and Registration Options

5
Alef Carlos