Как кешировать данные в приложении MVC - PullRequest
238 голосов
/ 05 декабря 2008

Я прочитал много информации о кэшировании страниц и частичном кэшировании страниц в приложении MVC. Тем не менее, я хотел бы знать, как вы будете кэшировать данные.

В моем сценарии я буду использовать LINQ to Entities (каркас сущностей). При первом вызове GetNames (или любого другого метода) я хочу получить данные из базы данных. Я хочу сохранить результаты в кэше и при втором вызове использовать кэшированную версию, если она существует.

Может кто-нибудь показать пример того, как это будет работать, где это должно быть реализовано (модель?) И будет ли это работать.

Я видел, как это делается в традиционных приложениях ASP.NET, обычно для очень статических данных.

Ответы [ 14 ]

383 голосов
/ 08 декабря 2008

Вот хороший и простой кеш-класс / сервис, который я использую:

using System.Runtime.Caching;  

public class InMemoryCache: ICacheService
{
    public T GetOrSet<T>(string cacheKey, Func<T> getItemCallback) where T : class
    {
        T item = MemoryCache.Default.Get(cacheKey) as T;
        if (item == null)
        {
            item = getItemCallback();
            MemoryCache.Default.Add(cacheKey, item, DateTime.Now.AddMinutes(10));
        }
        return item;
    }
}

interface ICacheService
{
    T GetOrSet<T>(string cacheKey, Func<T> getItemCallback) where T : class;
}

Использование:

cacheProvider.GetOrSet("cache key", (delegate method if cache is empty));

Поставщик кэша проверит, есть ли что-нибудь под именем «id кэша» в кэше, а если нет, он вызовет метод делегата для извлечения данных и сохранения их в кэше.

Пример:

var products=cacheService.GetOrSet("catalog.products", ()=>productRepository.GetAll())
73 голосов
/ 05 декабря 2008

Ссылка на dll System.Web в вашей модели и использование System.Web.Caching.Cache

    public string[] GetNames()
    {
      string[] names = Cache["names"] as string[];
      if(names == null) //not in cache
      {
        names = DB.GetNames();
        Cache["names"] = names;
      }
      return names;
    }

Немного упрощенно, но, думаю, это сработает. Это не специфично для MVC, и я всегда использовал этот метод для кэширования данных.

42 голосов
/ 08 июля 2009

Я имею в виду пост ТТ и предлагаю следующий подход:

Ссылка на dll System.Web в вашей модели и использование System.Web.Caching.Cache

public string[] GetNames()
{ 
    var noms = Cache["names"];
    if(noms == null) 
    {    
        noms = DB.GetNames();
        Cache["names"] = noms; 
    }

    return ((string[])noms);
}

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

Таким образом, вы добавляете данные, считанные из базы данных, и возвращаете их напрямую, не перечитывая из кеша.

28 голосов
/ 14 декабря 2015

Для .NET 4.5+ framework

добавить ссылку: System.Runtime.Caching

добавить оператор использования: using System.Runtime.Caching;

public string[] GetNames()
{ 
    var noms = System.Runtime.Caching.MemoryCache.Default["names"];
    if(noms == null) 
    {    
        noms = DB.GetNames();
        System.Runtime.Caching.MemoryCache.Default["names"] = noms; 
    }

    return ((string[])noms);
}

В .NET Framework 3.5 и более ранних версиях ASP.NET предоставлял реализацию кэша в памяти в пространстве имен System.Web.Caching. В предыдущих версиях .NET Framework кэширование было доступно только в пространстве имен System.Web и поэтому требовало зависимости от классов ASP.NET. В .NET Framework 4 пространство имен System.Runtime.Caching содержит API-интерфейсы, предназначенные как для веб-приложений, так и для других.

Дополнительная информация:

25 голосов
/ 04 июня 2011

Стив Смит сделал два отличных поста в блоге, которые демонстрируют, как использовать его шаблон CachedRepository в ASP.NET MVC. Он эффективно использует шаблон хранилища и позволяет вам получать кэширование без необходимости изменять существующий код.

http://ardalis.com/Introducing-the-CachedRepository-Pattern

http://ardalis.com/building-a-cachedrepository-via-strategy-pattern

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

4 голосов
/ 16 июля 2013

Кэширование AppFabric - это метод кэширования в памяти, который хранит данные в парах ключ-значение с использованием физической памяти на нескольких серверах. AppFabric обеспечивает улучшения производительности и масштабируемости для приложений .NET Framework. Концепции и архитектура

3 голосов
/ 01 июня 2016
public sealed class CacheManager
{
    private static volatile CacheManager instance;
    private static object syncRoot = new Object();
    private ObjectCache cache = null;
    private CacheItemPolicy defaultCacheItemPolicy = null;

    private CacheEntryRemovedCallback callback = null;
    private bool allowCache = true;

    private CacheManager()
    {
        cache = MemoryCache.Default;
        callback = new CacheEntryRemovedCallback(this.CachedItemRemovedCallback);

        defaultCacheItemPolicy = new CacheItemPolicy();
        defaultCacheItemPolicy.AbsoluteExpiration = DateTime.Now.AddHours(1.0);
        defaultCacheItemPolicy.RemovedCallback = callback;
        allowCache = StringUtils.Str2Bool(ConfigurationManager.AppSettings["AllowCache"]); ;
    }
    public static CacheManager Instance
    {
        get
        {
            if (instance == null)
            {
                lock (syncRoot)
                {
                    if (instance == null)
                    {
                        instance = new CacheManager();
                    }
                }
            }

            return instance;
        }
    }

    public IEnumerable GetCache(String Key)
    {
        if (Key == null || !allowCache)
        {
            return null;
        }

        try
        {
            String Key_ = Key;
            if (cache.Contains(Key_))
            {
                return (IEnumerable)cache.Get(Key_);
            }
            else
            {
                return null;
            }
        }
        catch (Exception)
        {
            return null;
        }
    }

    public void ClearCache(string key)
    {
        AddCache(key, null);
    }

    public bool AddCache(String Key, IEnumerable data, CacheItemPolicy cacheItemPolicy = null)
    {
        if (!allowCache) return true;
        try
        {
            if (Key == null)
            {
                return false;
            }

            if (cacheItemPolicy == null)
            {
                cacheItemPolicy = defaultCacheItemPolicy;
            }

            String Key_ = Key;

            lock (Key_)
            {
                return cache.Add(Key_, data, cacheItemPolicy);
            }
        }
        catch (Exception)
        {
            return false;
        }
    }

    private void CachedItemRemovedCallback(CacheEntryRemovedArguments arguments)
    {
        String strLog = String.Concat("Reason: ", arguments.RemovedReason.ToString(), " | Key-Name: ", arguments.CacheItem.Key, " | Value-Object: ", arguments.CacheItem.Value.ToString());
        LogManager.Instance.Info(strLog);
    }
}
3 голосов
/ 14 августа 2015

Вот улучшение ответа Хрвое Худо. Эта реализация имеет несколько ключевых улучшений:

  • Ключи кеша создаются автоматически на основе функции обновления данных и переданного объекта, который задает зависимости
  • Проход времени для любой длительности кэша
  • Использует замок для безопасности потока

Обратите внимание, что это зависит от Newtonsoft.Json для сериализации объекта depenOn, но его можно легко заменить любым другим методом сериализации.

ICache.cs

public interface ICache
{
    T GetOrSet<T>(Func<T> getItemCallback, object dependsOn, TimeSpan duration) where T : class;
}

InMemoryCache.cs

using System;
using System.Reflection;
using System.Runtime.Caching;
using Newtonsoft.Json;

public class InMemoryCache : ICache
{
    private static readonly object CacheLockObject = new object();

    public T GetOrSet<T>(Func<T> getItemCallback, object dependsOn, TimeSpan duration) where T : class
    {
        string cacheKey = GetCacheKey(getItemCallback, dependsOn);
        T item = MemoryCache.Default.Get(cacheKey) as T;
        if (item == null)
        {
            lock (CacheLockObject)
            {
                item = getItemCallback();
                MemoryCache.Default.Add(cacheKey, item, DateTime.Now.Add(duration));
            }
        }
        return item;
    }

    private string GetCacheKey<T>(Func<T> itemCallback, object dependsOn) where T: class
    {
        var serializedDependants = JsonConvert.SerializeObject(dependsOn);
        var methodType = itemCallback.GetType();
        return methodType.FullName + serializedDependants;
    }
}

Использование:

var order = _cache.GetOrSet(
    () => _session.Set<Order>().SingleOrDefault(o => o.Id == orderId)
    , new { id = orderId }
    , new TimeSpan(0, 10, 0)
);
3 голосов
/ 08 февраля 2015

Расширение ответа @Hrvoje Hudo ...

Код:

using System;
using System.Runtime.Caching;

public class InMemoryCache : ICacheService
{
    public TValue Get<TValue>(string cacheKey, int durationInMinutes, Func<TValue> getItemCallback) where TValue : class
    {
        TValue item = MemoryCache.Default.Get(cacheKey) as TValue;
        if (item == null)
        {
            item = getItemCallback();
            MemoryCache.Default.Add(cacheKey, item, DateTime.Now.AddMinutes(durationInMinutes));
        }
        return item;
    }

    public TValue Get<TValue, TId>(string cacheKeyFormat, TId id, int durationInMinutes, Func<TId, TValue> getItemCallback) where TValue : class
    {
        string cacheKey = string.Format(cacheKeyFormat, id);
        TValue item = MemoryCache.Default.Get(cacheKey) as TValue;
        if (item == null)
        {
            item = getItemCallback(id);
            MemoryCache.Default.Add(cacheKey, item, DateTime.Now.AddMinutes(durationInMinutes));
        }
        return item;
    }
}

interface ICacheService
{
    TValue Get<TValue>(string cacheKey, Func<TValue> getItemCallback) where TValue : class;
    TValue Get<TValue, TId>(string cacheKeyFormat, TId id, Func<TId, TValue> getItemCallback) where TValue : class;
}

Примеры

Кэширование отдельного элемента (когда каждый элемент кэшируется на основе его идентификатора, поскольку кэширование всего каталога для типа элемента будет слишком интенсивным).

Product product = cache.Get("product_{0}", productId, 10, productData.getProductById);

Кэширование всего чего-либо

IEnumerable<Categories> categories = cache.Get("categories", 20, categoryData.getCategories);

Почему TId

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

1 голос
/ 27 декабря 2013

Я использую два класса. Первый объект ядра кеша:

public class Cacher<TValue>
    where TValue : class
{
    #region Properties
    private Func<TValue> _init;
    public string Key { get; private set; }
    public TValue Value
    {
        get
        {
            var item = HttpRuntime.Cache.Get(Key) as TValue;
            if (item == null)
            {
                item = _init();
                HttpContext.Current.Cache.Insert(Key, item);
            }
            return item;
        }
    }
    #endregion

    #region Constructor
    public Cacher(string key, Func<TValue> init)
    {
        Key = key;
        _init = init;
    }
    #endregion

    #region Methods
    public void Refresh()
    {
        HttpRuntime.Cache.Remove(Key);
    }
    #endregion
}

Второй - список объектов кеша:

public static class Caches
{
    static Caches()
    {
        Languages = new Cacher<IEnumerable<Language>>("Languages", () =>
                                                          {
                                                              using (var context = new WordsContext())
                                                              {
                                                                  return context.Languages.ToList();
                                                              }
                                                          });
    }
    public static Cacher<IEnumerable<Language>> Languages { get; private set; }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...