53 votes

Comment mettre en cache des objets dans ASP.NET MVC?

J'aimerais en cache les objets dans ASP.NET MVC. J'ai un BaseController que je veux tous les Contrôleurs hérite. Dans le BaseController il y a un User de la propriété qui permet de saisir les données de l'Utilisateur à partir de la base de données afin que je puisse l'utiliser dans le contrôleur, ou la passer le point de vue.

J'aimerais cache cette information. Je suis l'aide de cette information sur chaque page, donc il n'y a pas besoin d'aller à la base de données à chaque demande de page.

J'aimerais quelque chose comme:

if(_user is null)
  GrabFromDatabase
  StuffIntoCache
return CachedObject as User

Comment puis-je mettre en œuvre simple de mise en cache dans ASP.NET MVC?

76voto

Will Points 76760

Vous pouvez toujours utiliser le cache (qui est partagé entre l'ensemble des réponses) et de la session (unique par utilisateur) pour le stockage.

J'aime le suivant "essayez d'obtenir à partir du cache/créer et de stocker des" pattern (c#-comme le pseudo-code):

public static class CacheExtensions
{
  public static T GetOrStore<T>(this Cache cache, string key, Func<T> generator)
  {
    var result = cache[key];
    if(result == null)
    {
      result = generator();
      cache[key] = result;
    }
    return (T)result;
  }
}

vous devriez utiliser ceci comme suit:

var user = HttpRuntime
              .Cache
              .GetOrStore<User>(
                 string.Format("User{0}", _userId), 
                 () => Repository.GetUser(_userId));

Vous pouvez adapter ce modèle à la Session, ViewState (pouah) ou de tout autre mécanisme de cache. Vous pouvez également étendre la ControllerContext.HttpContext (qui je pense est l'un des wrappers dans le Système.Web.Extensions), ou créer une nouvelle classe de le faire avec de la place pour se moquer de la cache.

59voto

njappboy Points 848

J'ai pris la réponse de Will et l'ai modifiée pour rendre la classe CacheExtensions statique et pour suggérer une légère modification afin de tenir compte de la possibilité que Func<T> soit null :

 public static class CacheExtensions
{

    private static object sync = new object();
    public const int DefaultCacheExpiration = 20;

    /// <summary>
    /// Allows Caching of typed data
    /// </summary>
    /// <example><![CDATA[
    /// var user = HttpRuntime
    ///   .Cache
    ///   .GetOrStore<User>(
    ///      string.Format("User{0}", _userId), 
    ///      () => Repository.GetUser(_userId));
    ///
    /// ]]></example>
    /// <typeparam name="T"></typeparam>
    /// <param name="cache">calling object</param>
    /// <param name="key">Cache key</param>
    /// <param name="generator">Func that returns the object to store in cache</param>
    /// <returns></returns>
    /// <remarks>Uses a default cache expiration period as defined in <see cref="CacheExtensions.DefaultCacheExpiration"/></remarks>
    public static T GetOrStore<T>( this Cache cache, string key, Func<T> generator ) {
        return cache.GetOrStore( key, (cache[key] == null && generator != null) ? generator() : default( T ), DefaultCacheExpiration );
    }


    /// <summary>
    /// Allows Caching of typed data
    /// </summary>
    /// <example><![CDATA[
    /// var user = HttpRuntime
    ///   .Cache
    ///   .GetOrStore<User>(
    ///      string.Format("User{0}", _userId), 
    ///      () => Repository.GetUser(_userId));
    ///
    /// ]]></example>
    /// <typeparam name="T"></typeparam>
    /// <param name="cache">calling object</param>
    /// <param name="key">Cache key</param>
    /// <param name="generator">Func that returns the object to store in cache</param>
    /// <param name="expireInMinutes">Time to expire cache in minutes</param>
    /// <returns></returns>
    public static T GetOrStore<T>( this Cache cache, string key, Func<T> generator, double expireInMinutes ) {
        return cache.GetOrStore( key,  (cache[key] == null && generator != null) ? generator() : default( T ), expireInMinutes );
    }


    /// <summary>
    /// Allows Caching of typed data
    /// </summary>
    /// <example><![CDATA[
    /// var user = HttpRuntime
    ///   .Cache
    ///   .GetOrStore<User>(
    ///      string.Format("User{0}", _userId),_userId));
    ///
    /// ]]></example>
    /// <typeparam name="T"></typeparam>
    /// <param name="cache">calling object</param>
    /// <param name="key">Cache key</param>
    /// <param name="obj">Object to store in cache</param>
    /// <returns></returns>
    /// <remarks>Uses a default cache expiration period as defined in <see cref="CacheExtensions.DefaultCacheExpiration"/></remarks>
    public static T GetOrStore<T>( this Cache cache, string key, T obj ) {
        return cache.GetOrStore( key, obj, DefaultCacheExpiration );
    }

    /// <summary>
    /// Allows Caching of typed data
    /// </summary>
    /// <example><![CDATA[
    /// var user = HttpRuntime
    ///   .Cache
    ///   .GetOrStore<User>(
    ///      string.Format("User{0}", _userId), 
    ///      () => Repository.GetUser(_userId));
    ///
    /// ]]></example>
    /// <typeparam name="T"></typeparam>
    /// <param name="cache">calling object</param>
    /// <param name="key">Cache key</param>
    /// <param name="obj">Object to store in cache</param>
    /// <param name="expireInMinutes">Time to expire cache in minutes</param>
    /// <returns></returns>
    public static T GetOrStore<T>( this Cache cache, string key, T obj, double expireInMinutes ) {
        var result = cache[key];

        if ( result == null ) {

            lock ( sync ) {
                if ( result == null ) {
                    result = obj != null ? obj : default( T );
                    cache.Insert( key, result, null, DateTime.Now.AddMinutes( expireInMinutes ), Cache.NoSlidingExpiration );
                }
            }
        }

        return (T)result;

    }

}
 

Je souhaiterais également envisager d'aller plus loin dans la mise en œuvre d'une solution de session testable qui étend la classe abstraite System.Web.HttpSessionStateBase.

 public static class SessionExtension
{
    /// <summary>
    /// 
    /// </summary>
    /// <example><![CDATA[
    /// var user = HttpContext
    ///   .Session
    ///   .GetOrStore<User>(
    ///      string.Format("User{0}", _userId), 
    ///      () => Repository.GetUser(_userId));
    ///
    /// ]]></example>
    /// <typeparam name="T"></typeparam>
    /// <param name="cache"></param>
    /// <param name="key"></param>
    /// <param name="generator"></param>
    /// <returns></returns>
    public static T GetOrStore<T>( this HttpSessionStateBase session, string name, Func<T> generator ) {

        var result = session[name];
        if ( result != null )
            return (T)result;

        result = generator != null ? generator() : default( T );
        session.Add( name, result );
        return (T)result;
    }

}
 

6voto

Runscope API Tools Points 43859

Si vous voulez qu'il soit mis en cache pour la longueur de la demande, placez-le dans la classe de base de votre contrôleur:

 public User User {
    get {
    	User _user = ControllerContext.HttpContext.Items["user"] as User;

    	if (_user == null) {
    		_user = _repository.Get<User>(id);
    		ControllerContext.HttpContext.Items["user"] = _user;
    	}

    	return _user;
    }
}
 

Si vous souhaitez mettre en cache plus longtemps, utilisez l’appel de remplacement de l’appel ControllerContext par un pour mettre Cache []. Si vous choisissez d'utiliser l'objet Cache pour mettre en cache plus longtemps, vous devez utiliser une clé de cache unique car elle sera partagée entre les demandes / utilisateurs.

3voto

Matthew Points 1907

Je tiens à cacher le fait que les données sont mises en cache dans le référentiel. Vous pouvez accéder à la cache à travers les HttpContext.Actuel.Propriété du Cache et de stocker les informations de l'Utilisateur à l'aide de "l'Utilisateur"+id.ToString() comme clé.

Cela signifie que tous les accès aux données de l'Utilisateur à partir du référentiel permettra d'utiliser les données en cache, si disponible, et ne nécessite aucune modification de code dans le modèle, le contrôleur, le ou la vue.

J'ai utilisé cette méthode pour remédier aux graves problèmes de performances sur un système d'interrogation de la base de données pour chaque propriété de l'Utilisateur et réduit les temps de chargement des pages de quelques minutes à un seul chiffre secondes.

2voto

Mehrdad Afshari Points 204872

Si vous n'avez pas besoin spécifique invalidation des caractéristiques de ASP.NET la mise en cache, les champs statiques sont très bon, léger et facile à utiliser. Cependant, dès que vous avez besoin des fonctionnalités avancées, vous pouvez passer à ASP.NET s' Cache objet pour le stockage.

L'approche que j'utilise est de créer une propriété et un private champ. Si le champ est - null, le bien le remplir et de le retourner. J'ai également fournir un InvalidateCache méthode qui définit manuellement le champ d' null. L'avantage de cette approche que le mécanisme de mise en cache est encapsulé dans la propriété, et vous pouvez passer à une approche différente si vous le souhaitez.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X