Mejor forma de almacenar en caché un Modelo

asp.net asp.net-mvc asp.net-mvc-5 dapper

Pregunta

El método getAll de mi repositorio simple:

    public List<ListModel> GetAllLists()
    {
            using (MySqlConnection connection = new MySqlConnection(this.connectionString))
            {
                return connection.Query<ListModel>("SELECT * FROM projectx.lists").AsList();
            }

    }

Estoy usando esta clase que he encontrado aquí para manejar el almacenamiento en caché:

    public class CacheUtils : ICacheService
    {    
        public TValue Get<TValue>(string cacheKey, Func<TValue> getItemCallback, double durationInMinutes = 120) where TValue : class
        {

            TValue item = MemoryCache.Default.Get(cacheKey) as TValue;
            if (item == null)
            {
                Debug.WriteLine("Not cached");
                item = getItemCallback();
                MemoryCache.Default.Add(cacheKey, item, DateTime.Now.AddMinutes(durationInMinutes));
            }
            else
                Debug.WriteLine("Cached!");
            return item;
        }

        public TValue Get<TValue, TId>(string cacheKeyFormat, TId id, Func<TId, TValue> getItemCallback, double durationInMinutes = 120) 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;
        }
    }

Controlador casero:

    public ActionResult Index()
    {
        ListRepository listRep = new ListRepository();
        CacheUtils cache = new CacheUtils();
        return View(cache.Get("lists", listRep.GetAllLists));
    }

Pregunta, ¿hay una forma mejor de manejar el caché que llamar al ayudante desde el controlador? Idealmente, debería estar dentro del método de repositorio. Pero, ¿necesito repetir la verificación de los datos de caché existentes en cada método del repositorio? Es decir.:

    public List<ListModel> GetAllLists()
    {
        var lists = Cache["lists"];
        if(lists == null)
        {
            using (MySqlConnection connection = new MySqlConnection(this.connectionString))
            {
                lists = connection.Query<ListModel>("SELECT * FROM projectx.lists").AsList();
            }

            Cache["lists"] = lists;
        }
        return ((List<ListModel>)lists);
    }

Respuesta aceptada

Utilice un patrón de decorador y no contamine el negocio o la interfaz de usuario con la lógica de almacenamiento en caché. Arreglarlo con algo así como ninject (o bastardos pobres si no quieres agregar un DI) Te recomiendo marcarlo como una sola instancia.

Beneficios incluidos:

  • Agregar un método invalidante como void Save (ListModel) es fácil de invalidar la caché.
  • La capa superior y la capa inferior no saben nada sobre el hecho de que se almacenaron en caché.
  • También puede decorar de nuevo para agregar el registro, creación de perfiles, etc.
  • También puedes controlar el ciclo de vida del caché
  • usted no contamina el nivel del controlador con la lógica de almacenamiento en caché
  • fácil de quitar

Así que algo como lo de abajo funcionaría. Para saber cómo agregar también decoradores en ninject, consulte https://stackoverflow.com/a/8910599/1073280

public class MyHomeController
{
    private readonly IListCrud _listcrud;

    public MyHomeController(IListCrud listcrud)
    {
        _listcrud = listcrud;
    }

    public ActionResult Index()
    {
        return View(_listcrud.GetAllLists());
    }
}

public interface IListCrud
{
    List<ListModel> GetAllLists();
}

public class ListCrud : IListCrud
{
    public List<ListModel> GetAllLists()
    {
        using (MySqlConnection connection = new MySqlConnection(this.connectionString))
        {
            return connection.Query<ListModel>("SELECT * FROM projectx.lists").AsList();
        }
    }
}

public class ListCrudCache : IListCrud
{
    private readonly ICacheService _cache;
    private readonly IListCrud _inner;

    public ListCrudCache(ICacheService cache, IListCrud inner)
    {
        _cache = cache;
        _inner = inner;
    }

    public List<ListModel> GetAllLists()
    {
        return _cache.Get("lists", _inner.GetAllLists);
    }
}

Opinión: tal vez solo para mantener el código pequeño, pero tenga cuidado al seleccionar * con un ORM. si alguien cambia el nombre o quita una columna, no tendrá ningún mecanismo de prueba / detección de fallas fácil de detectar.


Respuesta popular

En mi opinión, no debería estar en el repositorio, ya que (para mí) huele a violación o SRP. El almacenamiento en caché debe ser un servicio de nivel superior sobre el repositorio.

Debe pensar en qué necesita realmente los beneficios del almacenamiento en caché. Si el almacenamiento en caché es para acelerar la interfaz de la API WEB, tenerla en el controlador es la mejor manera. Si necesita almacenar en caché en otro lugar también, considere la posibilidad de introducir algunas clases de servicio de capa intermedia y poner el almacenamiento en caché allí, pero siempre lo haría opcional de alguna manera.



Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué