Наиболее эффективный способ очистки кэша с использованием ASP.NET - PullRequest
19 голосов
/ 02 июня 2011

Я создаю веб-сайт на базе ASP.NET/Umbraco, который представляет собой очень персонализированные данные, управляемые через структуру сущностей, и нам приходится кэшировать довольно много запросов данных (например, поиск по ключевым словам), так как это загруженный сайт.

Но когда пользователь создает новую запись данных, мне нужно очистить все кэшированные запросы (поиски и т. Д.), Чтобы новая запись была доступна в результатах.

Итак, в моем создании,методы удаления и обновления Я вызываю следующий метод:

public static void ClearCacheItems()
{
    var enumerator = HttpContext.Current.Cache.GetEnumerator();

    while (enumerator.MoveNext())
    {
        HttpContext.Current.Cache.Remove(enumerator.Key.ToString());
    }
}

Это действительно плохо?Я не вижу, как еще я должен очистить кэшированные объекты?

Ответы [ 5 ]

11 голосов
/ 02 июня 2011

Метод, который вы используете, на самом деле является правильным способом очистки вашего кеша, в вашем коде есть только одна незначительная «ошибка».Перечислитель действителен только , пока исходная коллекция остается неизменной .Таким образом, хотя код может работать большую часть времени, в некоторых ситуациях могут быть небольшие ошибки.Лучше всего использовать следующий код, который делает то же самое, но не использует перечислитель напрямую.

List<string> keys = new List<string>();
IDictionaryEnumerator enumerator = Cache.GetEnumerator();

while (enumerator.MoveNext())
  keys.Add(enumerator.Key.ToString());

for (int i = 0; i < keys.Count; i++)
  Cache.Remove(keys[i]);
7 голосов
/ 02 июня 2011

Очистка всего кэша ASP.NET только для одного конкретного функционального домена выглядит несколько излишне.

Вы можете создать промежуточный объект и хранить там все свои кэшированные запросы. Этот объект может быть просто оболочкой для словарного объекта. Все запросы должны использовать этот объект вместо непосредственного воспроизведения с кешем ASP.NET.

Затем вы добавляете этот объект в ASP.NET Cache, когда вам это нужно. Когда вам нужно очистить запросы, просто перейдите к этому объекту и очистите основной словарь. Вот пример внедрения:

    public sealed class IntermediateCache<T>
    {
        private Dictionary<string, T> _dictionary = new Dictionary<string, T>();

        private IntermediateCache()
        {
        }

        public static IntermediateCache<T> Current
        {
            get
            {
                string key = "IntermediateCache|" + typeof(T).FullName;
                IntermediateCache<T> current = HttpContext.Current.Cache[key] as IntermediateCache<T>;
                if (current == null)
                {
                    current = new IntermediateCache<T>();
                    HttpContext.Current.Cache[key] = current;
                }
                return current;
            }
        }

        public T Get(string key, T defaultValue)
        {
            if (key == null)
                throw new ArgumentNullException("key");

            T value;
            if (_dictionary.TryGetValue(key, out value))
                return value;

            return defaultValue;
        }

        public void Set(string key, T value)
        {
            if (key == null)
                throw new ArgumentNullException("key");

            _dictionary[key] = value;
        }

        public void Clear()
        {
            _dictionary.Clear();
        }
    }

Если мой запрос представлен так:

    public class MyQueryObject
    {
       ....
    }

Тогда я бы использовал "региональный" кеш, например:

// put something in this intermediate cache
IntermediateCache<MyQueryObject>.Current.Set("myKey", myObj);

// clear this cache
IntermediateCache<MyQueryObject>.Current.Clear();
6 голосов
/ 02 июня 2011

Разработчикам класса Cache было бы очень легко добавить к нему метод Clear.Но они этого не сделали, и это было разработано - следовательно, ваш код плохой .

Одна проблема заключается в поточных последствиях перечисления через коллекцию , если коллекция модифицирована.Это приведет к ошибке.

Вам действительно никогда не нужно очищать весь кеш.Если серверу нужна память, он его очистит.Доступ к кэшу по ключу (по причине), поэтому вам необходимо знать, к чему вы пытаетесь получить доступ.Так что, если вам нужно удалить и элемент, сделать это с помощью ключа.

ОБНОВЛЕНИЕ

Мой совет будет создать кэш таким образом, чтобы очистить кэш было бы очень легко.Например, сгруппируйте элементы кэша (создайте класс для хранения связанных результатов кэша) по идентификатору и используйте идентификатор в качестве ключа.Всякий раз, когда что-то связанное с этим идентификатором изменяется, очищайте кэш для этого идентификатора. Легко, peasy.

3 голосов
/ 02 июня 2011

Да, никогда не проходите через кеш и не удаляйте элементы.Это даст вам мир боли (я прошел через это недавно).Поскольку вы получите сообщение об ошибке «коллекция была изменена, перечисление может не выполняться».

У меня также есть кэш для поиска, и я не устанавливаю срок действия - я вручную отменяю действие при необходимости.

Хитрость в том, что данные хранятся в «ведрах».Этот «сегмент» обычно является родительским идентификатором или, если вы хотите получить в терминах базы данных, внешним ключом.

Так что, если вам нужно очистить все «Заказы» для данного «Клиента», ключ кешадолжен быть CustomerId.В моем случае я кеширую ReadOnlyCollection<T>.Поэтому мне не нужно зацикливаться, просто удалите его, а затем добавьте свежую копию.

Кроме того, чтобы быть вдвойне безопасным, я использую ReaderWriterLockSlim для аннулирования кэша.Когда я знаю, что он должен быть признан недействительным, я сначала вызываю базу данных, получаю блокировку записи, очищаю и обновляю соответствующий кэш, а затем закрываю блокировку записи.

Таким образом, все без проблем.

3 голосов
/ 02 июня 2011

Рассматривали ли вы использование Cache Dependency ? Вот объяснение MSDN и несколько отрывков:

Устанавливает зависимость отношения между элементом, хранящимся в объекте Cache приложения ASP.NET, и файлом, ключом кеша,массив либо другого объекта CacheDependency.Класс CacheDependency отслеживает отношения зависимостей, так что при изменении любого из них кэшированный элемент будет автоматически удален.

// Insert the cache item.
CacheDependency dep = new CacheDependency(fileName, dt);
cache.Insert("key", "value", dep);

// Check whether CacheDependency.HasChanged is true.
if (dep.HasChanged)
  Response.Write("<p>The dependency has changed.");  
else Response.Write("<p>The dependency has not changed.");

И Этот очень увлеченный сотрудник объясняет еще немного оэто.

...