Améliorer les performances de vos applications MVC avec le caching

Louis-Guillaume MORAND (Traduction)

Dans ce tutoriel, vous allez apprendre comment vous pouvez radicalement améliorer les performances de votre application ASP.Net MVC en tirant avantage du cache de sortie. Vous allez apprendre comment mettre en cache le résultat retourné pas une action contrôleur pour que le même contenu n'ait pas besoin d'être regénéré à chaque invocation de l'action.

- Titre : Améliorer les performances de vos applications MVC avec le caching

- Auteur : Louis-Guillaume MORAND (Traduction)

- Parution : 1 mai 2009

- Licence : Copyright ® 2009 Equipe dotnet. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- Traduction

- Introduction

- Activer le cache de sortie

- Où le contenu est-il mis en cache?

- Faire varier le cache de sortie

- Créer un profil de cache

- Conclusion

- Remerciements

Traduction

Cet article est la traduction la plus fidèle possible de l'article original : Improving Performance with Output Caching

Introduction

L'objectif de ce tutoriel est d'expliquer comment vous pouvez radicalement améliorer les performances d'une application ASP.Net MVC en tirant avantage du cache de sortie (output caching). Le caching permet de mettre en cache le contenu retourné par une action de contrôleur. De cette façon, le même contenu n'a pas besoin d'être regénéré à chaque fois que l'action est invoquée.

Imaginez, par exemple, que votre application ASP.Net MVC affiche une liste d'enregistrements de base de données dans une vue nommée Index. Normalement, chaque fois qu'un utilisateur invoquer l'action contrôleur qui retourne la vue, les enregistrements doivent être récupérés en base en exécutant une requête.

Si, à l'opposé, vous tirez avantage du caching alors vous pouvez éviter d'exécuter une requête à chaque appel de la page. La vue peut être récupérée depuis le cache plutôt que d'être regénérée depuis l'action contrôleur. La mise en cache permet d'éviter du travail redondant et coûteux en performance sur le serveur.

Activer le cache de sortie

Vous activez le cache de sortie en ajoutant un attribut [OutputCache] soit individuellement à une action contrôleur, ou alors à une classe entière d'un contrôleur. Par exemple, le contrôleur du code suivant expose une action nommée Index(). La sortie de cette action est mise en cache pour 10 secondes.

Controllers\HomeController.cs

using System.Web.Mvc;

namespace MvcApplication1.Controllers
{
[HandleError]
public class HomeController : Controller
{
[OutputCache(Duration=10, VaryByParam="none")]
public ActionResult Index()
{
return View();
}

}
}

Dans les versions bêta d'ASP.Net, le cache de sortie ne fonctionne pas pour une URL de type http://www.MySite.com/. A la place, vous devez entrer une URL du type http://www.MySite.com/Home/Index.

Dans le code précédent, la durée de mise en cache est de 10 secondes. Par exemple, si vous voulez que la mise en cache soit d'une journée complète, alors pouvez spécifier une durée de cache de 86400 secondes (60 secondes * 60 minutes * 24 heures).

Il n'y a pas de garantie que le contenu soit mis en cache pour la quantité de temps que vous avez spécifié. Lorsque les ressources mémoires deviennent réduites, le cache commence à évincer le contenu automatiquement.

Le contrôleur Home dans le code précédent, retourne la vue Index précisée dans le code suivant. Il n'y a rien de spécial à propos de cette vue, elle affiche simplement l'heure courante.

Views\Home\Index.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Index.aspx.cs" Inherits="MvcApplication1.Views.Home.Index" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Index</title>
</head>
<body>
<div>

The current time is: <%= DateTime.Now.ToString("T") %>

</div>
</body>
</html>

La vue Index mise en cache

Si vous invoquez l'action Index() plusieurs fois en saisissant l'URL /Home/Index dans la barre d'adresse de votre navigateur et/ou cliquez plusieurs fois sur le bouton Rafraîchir, alors l'heure affichée sur la page ne changera qu'une fois toutes les 10 secondes. L'heure reste identique justement parce que la page est mise en cache.

Il est important de comprendre que la même vue est mise en cache pour chaque personne qui visite votre application. Toute personne qui invoque l'action Index() obtiendra la même version de la version en cache de la vue Index. Cela signifie que la quantité de travail que le serveur Web doit fournir est radicalement réduite.

La vue dans le code précédent agit de façon simple. Elle ne fait qu'afficher l'heure courante. Pourtant, vous auriez très bien pu vouloir afficher une liste d'enregistrements provenant d'une base de données. Dans ce cas, la liste d'enregistrements n'aurait pas besoin d'être récupérée depuis la base de données à chaque fois que l'action est invoquée. Ainsi, le caching permet également de réduire la charge de travail du serveur de base de données.

N'utilisez pas la directive <%@ OutputCache %> dans une vue MVC. Cette directive provient des applications Web Forms et ne doit pas être utilisée dans une application MVC.

Où le contenu est-il mis en cache?

Par défaut, lorsque vous utilisez l'attribut [OutputCache], le contenu est mis en cache à trois endroits: le serveur Web, chaque serveur proxy et le client (navigateur Web). Vous pouvez contrôler exactement où le cache sera placé en modifiant la propriété Location de l'attribut [OutputCache].

Vous pouvez définir la propriété Location avec n'importe laquelle des valeurs possibles suivantes :
- Any
- Client
- Downstream
- Server
- None
- ServerAndClient

Par défaut, la valeur de la propriété Location est Any. Pourtant, il existe des situations où vous pourriez souhaiter mettre en cache uniquement dans le navigateur Web ou uniquement sur le serveur Web. Par exemple, si vous mettez en cache des informations qui sont personnalisées pour chaque utilisateur alors vous ne devriez pas stocker le cache sur le serveur Web. Si vous affichez différentes informations à différents utilisateurs alors vous devriez mettre en cache uniquement sur le client.

Par exemple, le contrôleur défini dans le code suivant, expose une action nommée GetName() qui retourne le nom de l'utilisateur courant. Si jack se connecte sur l'application et invoque l'action GetName() alors l'action retourne la chaine "Hi Jack". Si, par la suite, Jill se connecte et appelle l'action GetName() alors elle obtiendra également la phrase "Hi Jack". La chaine étant mise en cache sur le serveur, tous les utilisateurs se connectant après Jack obtiendront la version mise en cache par Jack.

Controllers\BadUserController.cs

using System.Web.Mvc;
using System.Web.UI;

namespace MvcApplication1.Controllers
{
public class BadUserController : Controller
{
[OutputCache(Duration = 3600, VaryByParam = "none")]
public string GetName()
{
return "Hi " + User.Identity.Name;
}
}
}

Il est donc évident que le contrôleur ne fonctionne pas de la façon dont on le souhaiterait. Vous ne voulez pas afficher "Hi Jack" à l'utilisatrice Jill.

Vous ne devriez jamais mettre en cache du contenu personnalisé dans le cache serveur. Pourtant, vous pourriez vouloir mettre en cache le contenu personnalisé dans le cache du navigateur afin d'améliorer les performances. En effet, à l'appel de l'action, le contenu serait récupéré depuis le cache du navigateur plutôt que faire appel au serveur Web.

La version modifiée du contrôleur présenté dans le code suivant met également en cache le contenu retourné par l'action GetName(). Par contre, le contenu n'est mis en cache que dans le cache navigateur et pas sur le serveur. De cette façon, lorsque plusieurs utilisateurs invoquent l'action GetName(), chaque personne obtient son propre nom affiché et pas celui d'une autre personne.

Controllers\UserController.cs

using System.Web.Mvc;
using System.Web.UI;

namespace MvcApplication1.Controllers
{
public class UserController : Controller
{
[OutputCache(Duration=3600, VaryByParam="none", Location=OutputCacheLocation.Client, NoStore=true)]
public string GetName()
{
return "Hi " + User.Identity.Name;
}
}
}

Notez que l'attribut [OutputCache] inclut une propriété Location définie à la valeur OutputCacheLocation.Client.
L'attribut [OutputCache] inclut aussi une propriété NoStore. Cette propriété est utilisée pour informer les serveurs proxy et le navigateur qu'ils ne doivent pas stocker une copie permanente du contenu mis en cache.

Faire varier le cache de sortie

Dans certaines situations, vous pourriez vouloir différentes versions du même contenu. Imaginez par exemple que vous voulez créer une page maître/détails. La page maître affiche une liste de titre de films et lorsque vous cliquez sur un titre, vous obtenez les détails du film choisi.

Si vous mettez en cache la page détails alors les détails d'un même film seront affichez quelque soit le film que vous choisirez. Le premier film choisi sera affiché à tous les utilisateurs suivants.

Vous pouvez corriger ce problème en tirant avantage de la propriété VaryByParam de l'attribut [OutputCache]. Cette propriété vous autorise à créer différentes versions d'un même contenu, dès que l'un des paramètres du formulaire ou de l'URL change.

Par exemple, le contrôleur du code suivant exposé deux méthodes nommées Master() et Details(). L'action Master() retourne une liste de titres de films tandis que l'action Details retourne les details du film sélectionné.

Controllers\MoviesController.cs

using System.Linq;
using System.Web.Mvc;
using MvcApplication1.Models;

namespace MvcApplication1.Controllers
{
public class MoviesController : Controller
{
private MovieDataContext _dataContext;

public MoviesController()
{
_dataContext = new MovieDataContext();
}

[OutputCache(Duration=int.MaxValue, VaryByParam="none")]
public ActionResult Master()
{
ViewData.Model = (from m in _dataContext.Movies
select m).ToList();
return View();
}

[OutputCache(Duration = int.MaxValue, VaryByParam = "id")]
public ActionResult Details(int id)
{
ViewData.Model = _dataContext.Movies.SingleOrDefault(m => m.Id == id);
return View();
}

}
}

L'action Master() inclut une propriété VaryByParam avec la valeur "none". Lorsque l'action Master() est invoquée, la même version mise en cache de la vue Master est renvoyée. Tous les paramètres de formulaire ou de requête sont ignorés.

La vue Master

La vue Details

L'action Details inclut une propriété VaryByParam avec la valeur "Id". Lorsque différentes valeurs du paramètre Id sont passées à l'action contrôleur, différentes versions de la vue Details sont générées et mises en cache.

Il est important de comprendre qu'utiliser la propriété VaryByParam résulte par plus de cache et rien de moins. Une version en cache est créée pour chaque différente version du paramètre Id.

Vous pouvez utiliser la propriété VaryByParam avec les valeurs suivantes possibles :
* = Créer une version différente en cache dès qu'un paramètre de formulaire ou de requête change.
none = Ne jamais créer différentes versions en cache
Liste de paramètres = Créer une version différente en cache dès qu'un paramètre de formulaire ou de requête, appartenant à la liste, change.

Créer un profil de cache

En alternative à la configuration du cache via les propriétés de l'attribut [OutputCache], vous pouvez créer un profil de cache dans le fichier de configuration Web (web.config). Créer un profil de cache dans le fichier de configuration apporte deux avantages majeurs.

Tout d'abord, en configurant le cache dans le fichier de configuration Web, vous pouvez contrôler comment les actions contrôleurs mettent en cache le contenu, dans un unique endroit. Vous pouvez créer un profil de caching et l'appliquer à plusieurs contrôleurs ou actions contrôleur.

Deuxièmement, vous pouvez modifier le fichier de configuration Web sans recompiler votre application. Si vous avez besoin de désactiver la mise en cache pour une application qui a déjà été déployée en production alors vous pouvez simplement modifier le profil de cache défini dans le fichier de configuration Web. Tous les changements apportés au fichier de configuration seront automatiquement détectés et appliqués.

Par exemple, la section de configuration Web <caching> définit un profil de cache nommé Cache1Hour. Cette section <caching> doit apparaître dans la section <system.web> du fichier de configuration Web.

Caching section for web.config

<caching>
<outputCacheSettings>
<outputCacheProfiles>
<add name="Cache1Hour" duration="3600" varyByParam="none"/>
</outputCacheProfiles>
</outputCacheSettings>
</caching>

Le contrôleur suivant illustre comment vous pouvez appliquer le profil Cache1Hour sur une action contrôleur à l'aide de l'attribut [OutputCache].

Controllers\ProfileController.cs

using System;
using System.Web.Mvc;

namespace MvcApplication1.Controllers
{
public class ProfileController : Controller
{
[OutputCache(CacheProfile="Cache1Hour")]
public string Index()
{
return DateTime.Now.ToString("T");
}
}
}

Si vous invoquez l'action Index() exposée par le contrôleur alors la même heure sera retournée pendant une heure.

Conclusion

Le cache de sortie fournir un moyen simple pour améliorer radicalement les performances de vos applications ASP.Net MVC. Dans ce tutoriel, vous avez appris comment utiliser l'attribut [OutputCache] pour mettre en cache la sortie des actions contrôleur. Vous avez également appris comment modifier les propriétés de l'attribut [OutputCache] tel que les propriétés Duration et VaryByParam pour modifier comment le contenu est mis en cache. Pour finir, vous avez appris comment définir des profils de cache dans le fichier de configuration Web.

Remerciements

Merci à dourou05 pour les corrections apportées à l'article.