Ошибка ASP.NET Cache Absolute Expiration
Я сохраняю одно целое значение в HttpContext.Cache с абсолютным временем истечения 5 минут. Однако после ожидания 6 минут (или дольше) целочисленное значение все еще находится в кэше (т.е. Оно никогда не удаляется, хотя прошло абсолютное истечение). Вот код, который я использую:
public void UpdateCountFor(string remoteIp)
{
// only returns true the first time its run
// after that the value is still in the Cache
// even after the absolute expiration has passed
// so after that this keeps returning false
if (HttpContext.Current.Cache[remoteIp] == null)
{
// nothing for this ip in the cache so add the ip as a key with a value of 1
var expireDate = DateTime.Now.AddMinutes(5);
// I also tried:
// var expireDate = DateTime.UtcNow.AddMinutes(5);
// and that did not work either.
HttpContext.Current.Cache.Insert(remoteIp, 1, null, expireDate, Cache.NoSlidingExpiration, CacheItemPriority.Default, null);
}
else
{
// increment the existing value
HttpContext.Current.Cache[remoteIp] = ((int)HttpContext.Current.Cache[remoteIp]) + 1;
}
}
При первом запуске UpdateCountFor ( "127.0.0.1" ) он вставляет 1 в кеш с ключом "127.0.0.1" и абсолютное истечение через 5 минут, как и ожидалось. Каждый последующий запуск затем увеличивает значение в кеше. Однако, после ожидания 10 минут, он продолжает увеличивать значение в кеше. Значение никогда не истекает и никогда не удаляется из кэша. Почему это?
Я понимаю, что абсолютное время истечения означает, что элемент будет удален примерно в это время. Я делаю что-то неправильно? Я что-то не понимаю?
Я ожидаю, что значение будет удалено из кэша через 5 минут, однако оно остается там, пока я не перестрою проект.
Все это работает на .NET 4.0 на моей локальной машине.
Ответы
Ответ 1
Оказывается, что эта строка:
HttpContext.Current.Cache[remoteIp] = ((int)HttpContext.Current.Cache[remoteIp]) + 1;
удаляет предыдущее значение и повторно вставляет значение с абсолютным или скользящим сроком действия NO. Чтобы обойти это, мне пришлось создать класс-помощник и использовать его так:
public class IncrementingCacheCounter
{
public int Count;
public DateTime ExpireDate;
}
public void UpdateCountFor(string remoteIp)
{
IncrementingCacheCounter counter = null;
if (HttpContext.Current.Cache[remoteIp] == null)
{
var expireDate = DateTime.Now.AddMinutes(5);
counter = new IncrementingCacheCounter { Count = 1, ExpireDate = expireDate };
}
else
{
counter = (IncrementingCacheCounter)HttpContext.Current.Cache[remoteIp];
counter.Count++;
}
HttpContext.Current.Cache.Insert(remoteIp, counter, null, counter.ExpireDate, Cache.NoSlidingExpiration, CacheItemPriority.Default, null);
}
Это обострит проблему и позволит счетчику истечь в абсолютное время, сохраняя при этом обновления.
Ответ 2
Попробуйте использовать DateTime.UtcNow, чтобы рассчитать период ожидания, а не datetime.Now. Вы можете столкнуться с проблемой, описанной ниже:
absoluteExpiration Тип: System.DateTime. Время, в которое вставленный объект истекает и удаляется из кеша. Чтобы избежать возможных проблемы с местным временем, такие как изменения от стандартного времени до летнего времени времени, используйте UtcNow вместо Now for это значение параметра. Если вы используете абсолютное истечение, параметр slideExpiration должен быть NoSlidingExpiration.
Ответ 3
Там более простой ответ, чем опубликованный smoak. Используя этот пример в качестве отправной точки, обновленный код ниже работает и не требует повторной вставки. Причина этого в том, что классы являются ссылочными типами. Таким образом, при обновлении счетчика внутри экземпляра класса он не вызывает запуск кэша обновления.
public class IncrementingCacheCounter
{
public int Count;
}
public void UpdateCountFor(string remoteIp)
{
IncrementingCacheCounter counter = null;
if (HttpContext.Current.Cache[remoteIp] == null)
{
counter = new IncrementingCacheCounter { Count = 1};
HttpContext.Current.Cache.Insert(remoteIp, counter, null, DateTime.Now.AddMinutes(5), Cache.NoSlidingExpiration, CacheItemPriority.Default, null);
}
else
{
counter = (IncrementingCacheCounter)HttpContext.Current.Cache[remoteIp];
counter.Count++;
}
}