👌

Entity Frameworkのクエリはキャッシュに登録しない

2024/03/30に公開

C# で EntityFramework を使ってDBを呼び出すクエリは遅延評価されます。
そのクエリをキャッシュに登録すると遅延評価によって問題を引き起こすことがあります。

具体例

using (var context = new MyDbContext())
{
    var customers = context.Customers.Find(x => x.Area == "Tokyo");
    cache.Add("TokyoCustomers", customers);
    cache.Get(customers); // (1)
}

cache.Get("TokyoCustomers"); // (2)

上記のようなソースコードを書いて実行すると、(2)の処理で「操作を完了できません。DbContext は破棄されています。」とエラーが生じることあります。
理由は customers がDB呼び出しクエリの実行結果ではなくクエリ自体を保存しているためです。(2) で呼び出された際にクエリを実行するためにDBへの接続を行います。しかし、このタイミングでは context は破棄されているため、上記のエラーが発生します。

回避方法

キャッシュへの登録時に即時実行しておき、実行結果を登録することでこの問題を回避することができます。例えば、.ToList() を付けると回避できます。

cache.Add("TokyoCustomers", customers.ToList());

面倒な点としては、登録する値の型からは遅延評価か即時評価かは分からないということがあります。EFクエリでは登録する値の型は IEnumerableIQueryable だと思います。しかし、IEnumerableIQueryable だからと言って必ずしも遅延評価というわけではないです。インスタンスが遅延評価であるかどうかを確認する必要があります。

また、EntityFramework以外でも遅延評価される値をキャッシュに登録することで評価時に状況が変わって想定する動きをしないことがあります。

まとめ

遅延評価は必要になるまで実行しないことでパフォーマンスを向上させることができる、不要なメモリの使用を抑えることができるなどのメリットがあります。一方で上記のような問題が生じるデメリットもあります。

Discussion