it-swarm-pt.tech

Como armazenar dados em cache em um aplicativo MVC

Eu li muitas informações sobre o cache de páginas e o cache de página parcial em um aplicativo MVC. No entanto, gostaria de saber como você armazenaria dados em cache.

No meu cenário, vou usar o LINQ to Entities (framework de entidades). Na primeira chamada para GetNames (ou seja qual for o método) eu quero pegar os dados do banco de dados. Eu quero salvar os resultados em cache e na segunda chamada para usar a versão em cache, se existir.

Alguém pode mostrar um exemplo de como isso funcionaria, onde isso deveria ser implementado (modelo?) E se funcionaria.

Eu vi isso feito em aplicativos ASP.NET tradicionais, normalmente para dados muito estáticos.

233
Coolcoder

Referência a dll System.Web no seu modelo e use 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;
    }

Um pouco simplificado, mas acho que funcionaria. Isso não é específico do MVC e sempre usei esse método para armazenar dados em cache.

72
terjetyl

Aqui está uma classe/serviço auxiliar de cache agradável e simples que eu uso: 

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;
}

Uso:

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

O provedor de cache verificará se há alguma coisa com o nome "cache id" no cache e, se não houver, chamará um método delegado para buscar dados e armazená-los no cache. 

Exemplo:

var products=cacheService.GetOrSet("catalog.products", ()=>productRepository.GetAll())
374
Hrvoje Hudo

Estou me referindo ao post do TT e sugiro a seguinte abordagem:

Consulte a dll System.Web no seu modelo e use System.Web.Caching.Cache

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

    return ((string[])noms);
}

Você não deve retornar um valor relido do cache, pois você nunca saberá se, naquele momento específico, ele ainda está no cache. Mesmo se você inseriu na declaração antes, ela pode já ter desaparecido ou nunca ter sido adicionada ao cache - você simplesmente não sabe.

Então, você adiciona os dados lidos do banco de dados e os retorna diretamente, sem voltar a ler do cache. 

42
Oli

Para o framework .NET 4.5+

adicionar referência: System.Runtime.Caching 

adicione usando a instrução: 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);
}

No .NET Framework 3.5 e versões anteriores, o ASP.NET forneceu uma implementação de cache na memória no namespace System.Web.Caching. Nas versões anteriores do .NET Framework, o armazenamento em cache estava disponível apenas no namespace System.Web e, portanto, exigia uma dependência nas classes do ASP.NET. No .NET Framework 4, o namespace System.Runtime.Caching contém APIs projetadas para aplicativos Web e não Web. 

Mais informações:

27
juFo

Steve Smith fez dois ótimos posts que demonstram como usar seu padrão CachedRepository no ASP.NET MVC. Ele usa o padrão de repositório de forma eficaz e permite que você obtenha o armazenamento em cache sem ter que alterar seu código existente.

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

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

Nestes dois posts, ele mostra como configurar esse padrão e também explica por que é útil. Ao usar esse padrão, você obtém o armazenamento em cache sem que seu código existente veja qualquer lógica de armazenamento em cache. Essencialmente, você usa o repositório em cache como se fosse qualquer outro repositório.

24
Brendan Enrick

AppFabric Caching é distribuído e uma técnica de armazenamento em cache de memória que armazena dados em pares de valores-chave usando memória física em vários servidores. O AppFabric fornece melhorias de desempenho e escalabilidade para aplicativos .NET Framework. Conceitos e Arquitetura

4
Arun Duth
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
Chau

Aqui está uma melhoria para a resposta de Hrvoje Hudo. Esta implementação tem algumas melhorias importantes:

  • As chaves de cache são criadas automaticamente com base na função para atualizar dados e o objeto transmitido especifica dependências
  • Passar no intervalo de tempo para qualquer duração do cache
  • Usa um bloqueio para segurança de thread

Observe que isso tem uma dependência no Newtonsoft.Json para serializar o objeto dependsOn, mas que pode ser facilmente substituído por qualquer outro método de serialização.

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;
    }
}

Uso:

var order = _cache.GetOrSet(
    () => _session.Set<Order>().SingleOrDefault(o => o.Id == orderId)
    , new { id = orderId }
    , new TimeSpan(0, 10, 0)
);
3
DShook

Estendendo a resposta de @Hrvoje Hudo ...

Código:

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;
}

Exemplos

Armazenamento em cache de item único (quando cada item é armazenado em cache com base em seu ID porque o armazenamento em cache do catálogo inteiro para o tipo de item seria muito intensivo).

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

Armazenando tudo em cache

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

Por que TId

O segundo auxiliar é especialmente Nice porque a maioria das chaves de dados não é composta. Métodos adicionais podem ser adicionados se você usar chaves compostas com freqüência. Desta forma, você evita fazer todos os tipos de concatenação de cadeia ou string.Formats para obter a chave para passar para o auxiliar de cache. Ele também torna a passagem do método de acesso a dados mais fácil porque você não precisa passar o ID para o método wrapper ... a coisa toda se torna muito concisa e consistente para a maioria dos casos de uso.

3
smdrager

Eu uso duas classes. Primeiro, o objeto do núcleo do cache:

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
}

A segunda é a lista de objetos de cache:

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; }
}
1
Berezh

Eu usei desta forma e funciona para mim. https://msdn.Microsoft.com/en-us/library/system.web.caching.cache.add(v=vs. 110) .aspx Informações de parâmetros para 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;

}
0
user3776645
HttpContext.Current.Cache.Insert("subjectlist", subjectlist);
0
Md. Akhtar Uzzaman

Eu direi que implementar o Singleton nesse problema persistente de dados pode ser uma solução para esse problema caso você encontre soluções anteriores muito complicadas

 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
0
GeraGamo