外部 API を呼び出してデータを取得する MVC 3 アプリがあります。API は XML を返し、それをオブジェクトにシリアル化します。これらのオブジェクトの一部は 1 日を通して変更されないため、オーバーヘッドを減らすためにそれらをキャッシュします。私の問題は、アイテムをキャッシュから取り出して操作すると、キャッシュされた値も変更されることです。これは、キャッシュされたアイテムへの参照のみが返されるためだと思います。System.Web.HttpRuntime.Cache を使用してオブジェクトをキャッシュしています。不変オブジェクトとしてキャッシュからアイテムを返す方法はありますか? または、オブジェクトのクローンを簡単に返しますか? キャッシュされた値を XML に逆シリアル化し、それを新しいオブジェクトにシリアル化しようとしましたが、これは機能しますが、多くのオーバーヘッドが追加され、負荷がかかった状態でサイトのパフォーマンスが非常に低下します。アイテムごとに ICloneable を実装できますが、しかし、その後、クラスごとにディープ クローン メソッドを作成する必要があります。これには多くの時間がかかり、シリアライズと同じオーバーヘッドの問題が発生する可能性があります。もっと手っ取り早い方法はないか考え中です。
人のリストがキャッシュされているとしましょう。私がこれを行う場合:
var people = Cache.Get<List<Person>>("people");
people.Remove(p => p.LastName != "Smith");
return View(people);
これにより、ビューとキャッシュされたリストの両方から、Smith 以外の姓を持つすべての人がリストから削除されます。そのアイテムの今後の取得では、スミスのみが返されます。
私はこれを回避することができます:
var people = Cache.Get<List<Person>>("people");
var viewlist = people.Where(p => p.LastName == "Smith").ToList();
return View(viewlist);
これは、キャッシュされた値には影響しません。明らかに、コードのどこでもこれを行うことができますが、キャッシュしたアイテムの数を考えると、毎回これを確実に行う必要があります。キャッシュから取得したオブジェクトを編集しようとすると、コードがコンパイル時 (または実行時) にエラーをスローするようにできれば、はるかに優れています。
私のキャッシング方法:
static object lockobj = new object();
public static T Get<T>(string index) //where T : new()
{
T data;
lock (lockobj)
{
data = (T)_cache[index];
}
return data;
}
public static T CacheInsert<T>(T obj, string index, CacheDependency dependencies,
DateTime absoluteExpiration, TimeSpan slidingExpiration,
CacheItemPriority priority, CacheItemRemovedCallback callback)
{
T data;
lock (lockobj)
{
data = (T)_cache[index];
if (data == null)
{
_cache.Insert(index, obj, dependencies, absoluteExpiration, slidingExpiration, priority, callback);
data = obj;
}
}
return data;
}