Подтвердить что ты не робот

Самый эффективный способ очистки кэша с помощью ASP.NET

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

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

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

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

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

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

4b9b3361

Ответ 1

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

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]);

Ответ 2

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

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

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

    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();

Ответ 3

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

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

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

UPDATE

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

Ответ 4

Считаете ли вы использование Зависимости кэша? Здесь объяснение 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.");

И этот восторженный парень объясняет еще кое-что об этом.

Ответ 5

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

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

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

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

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

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