Я написал общий класс Cache, предназначенный для возврата объекта в памяти, и иногда оценивает src (IQueryable или функцию, возвращающую IQueryable). Его использование в нескольких местах в моем приложении, где получение большого количества данных через сущность framework является дорогостоящим.Класс кэша и измененные коллекции
Это называется
//class level
private static CachedData<Foo> fooCache= new CachedData<Foo>();
//method level
var results = fooCache.GetData("foos", fooRepo.Include("Bars"));
Хотя, казалось нормально работать в тестировании, работает на занятом веб-сервера я вижу некоторые проблемы с «Коллекция была изменена, операция перечисления не может выполнить.» ошибки в коде, который потребляет результаты.
Это должно быть потому, что один поток переписывает объект результатов внутри замка, а другой - за пределы замка.
Я предполагаю, что мое единственное решение состоит в том, чтобы вернуть копию результатов каждому потребителю, а не оригиналу, и что я не могу разрешить копирование во время блокировки Fetch, но это может происходить одновременно.
Может ли кто-нибудь предложить лучший способ или помочь в стратегии блокировки, пожалуйста?
public class CachedData<T> where T:class
{
private static Dictionary<string, IEnumerable<T>> DataCache { get; set; }
public static Dictionary<string, DateTime> Expire { get; set; }
public int TTL { get; set; }
private object lo = new object();
public CachedData()
{
TTL = 600;
Expire = new Dictionary<string, DateTime>();
DataCache = new Dictionary<string, IEnumerable<T>>();
}
public IEnumerable<T> GetData(string key, Func<IQueryable<T>> src)
{
var bc = brandKey(key);
if (!DataCache.ContainsKey(bc)) Fetch(bc, src);
if (DateTime.Now > Expire[bc]) Fetch(bc, src);
return DataCache[bc];
}
public IEnumerable<T> GetData(string key, IQueryable<T> src)
{
var bc = brandKey(key);
if ((!DataCache.ContainsKey(bc)) || (DateTime.Now > Expire[bc])) Fetch(bc, src);
return DataCache[bc];
}
private void Fetch(string key, IQueryable<T> src)
{
lock (lo)
{
if ((!DataCache.ContainsKey(key)) || (DateTime.Now > Expire[key])) ExecuteFetch(key, src);
}
}
private void Fetch(string key, Func<IQueryable<T>> src)
{
lock (lo)
{
if ((!DataCache.ContainsKey(key)) || (DateTime.Now > Expire[key])) ExecuteFetch(key, src());
}
}
private void ExecuteFetch(string key, IQueryable<T> src)
{
if (!DataCache.ContainsKey(key)) DataCache.Add(key, src.ToList());
else DataCache[key] = src.ToList();
if (!Expire.ContainsKey(key)) Expire.Add(key, DateTime.Now.AddSeconds(TTL));
else Expire[key] = DateTime.Now.AddSeconds(TTL);
}
private string brandKey(string key, int? brandid = null)
{
return string.Format("{0}/{1}", brandid ?? Config.BrandID, key);
}
}
Ваш кеш полностью не потокобезопасен и не должен использоваться. – SLaks
Да @SLaks Я знаю :-(! – Andiih