Как кешировать данные в приложении MVC

asp.net-mvc database caching

199119 просмотра

15 ответа

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

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

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

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

Автор: Coolcoder Источник Размещён: 12.11.2019 09:13

Ответы (15)


73 плюса

Решение

Ссылайтесь на 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, и я всегда использовал этот метод для кэширования данных.

Автор: terjetyl Размещён: 05.12.2008 02:10

392 плюса

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

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())
Автор: Hrvoje Hudo Размещён: 08.12.2008 10:34

43 плюса

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

Ссылайтесь на 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);
}

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

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

Автор: Oli Размещён: 08.07.2009 03:39

33 плюса

Для .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-интерфейсы, разработанные как для веб-приложений, так и для не-веб-приложений.

Больше информации:

Автор: juFo Размещён: 14.12.2015 10:31

26 плюса

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

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

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

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

Автор: Brendan Enrick Размещён: 03.06.2011 08:54

4 плюса

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

Автор: Arun Duth Размещён: 16.07.2013 03:00

3 плюса

Расширяя ответ @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);

Почему

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

Автор: smdrager Размещён: 08.02.2015 05:52

3 плюса

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

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

Обратите внимание, что это зависит от 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)
);
Автор: DShook Размещён: 14.08.2015 07:03

3 плюса

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);
    }
}
Автор: Chau Размещён: 01.06.2016 10:53

1 плюс

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

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; }
}
Автор: Berezh Размещён: 27.12.2013 01:32

1 плюс

Я использовал это таким образом, и это работает для меня. https://msdn.microsoft.com/en-us/library/system.web.caching.cache.add(v=vs.110).aspx информация о параметрах для system.web.caching.cache.add.

public string GetInfo()
{
     string name = string.Empty;
     if(System.Web.HttpContext.Current.Cache["KeyName"] == null)
     {
         name = GetNameMethod();
         System.Web.HttpContext.Current.Cache.Add("KeyName", name, null, DateTime.Noew.AddMinutes(5), Cache.NoSlidingExpiration, CacheitemPriority.AboveNormal, null);
     }
     else
     {
         name = System.Web.HttpContext.Current.Cache["KeyName"] as string;
     }

      return name;

}
Автор: user3776645 Размещён: 17.05.2018 09:17

0 плюса

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

 public class GPDataDictionary
{
    private Dictionary<string, object> configDictionary = new Dictionary<string, object>();

    /// <summary>
    /// Configuration values dictionary
    /// </summary>
    public Dictionary<string, object> ConfigDictionary
    {
        get { return configDictionary; }
    }

    private static GPDataDictionary instance;
    public static GPDataDictionary Instance
    {
        get
        {
            if (instance == null)
            {
                instance = new GPDataDictionary();
            }
            return instance;
        }
    }

    // private constructor
    private GPDataDictionary() { }

}  // singleton
Автор: GeraGamo Размещён: 05.11.2015 04:55

0 плюса

HttpContext.Current.Cache.Insert("subjectlist", subjectlist);
Автор: Md. Akhtar Uzzaman Размещён: 24.02.2018 03:38

0 плюса

Приведенное ниже решение Оли было одним из согласованных подходов. Я думал, что поскольку noms могут ссылаться на ненулевое значение для кэша при проверке на нулевое значение, не может ли noms ссылаться на другое значение, если срок действия кэша истек или стал каким-либо образом недействительным (возможно, нулевым) до того, как функция вернула результат? У меня такое чувство, что я что-то упускаю.

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

Спасибо!

Автор: KEn Rubin Размещён: 02.09.2019 03:02

-8 плюса

Вы также можете попробовать использовать кэширование, встроенное в ASP MVC:

Добавьте следующий атрибут в метод контроллера, который вы хотите кэшировать:

[OutputCache(Duration=10)]

В этом случае ActionResult этого будет кэшироваться в течение 10 секунд.

Подробнее об этом здесь

Автор: qui Размещён: 09.12.2008 05:12
Вопросы из категории :
32x32