Optimisation du ViewState - l'enregistrer sur le server via le SessionPageStatePersister
Il y a quelques temps je vous avez expliqué comment modifier la façon dont le viewstate est enregistré, nous avions vu qu'il fallait surcharger les méthodes SavePageStateToPersistenceMedium et LoadPageStateToPersistenceMedium. Je viens de découvrir que l'on peut faire encore mieux, il est également possible de surcharger la propriété PageStatePersister de l'objet Page.
Cette propriété de type PageStatePersister est une classe abstraite qui nécessite la surcharge de deux méthodes : Save et Load qui sauvegarde ou restaure l'état des contrôles. Il existe deux implémentations dans ASP.net, HiddenFieldPageStatePersister et SessionPageStatePersister. Par défaut le HiddenFieldPageStatePersister est utilisé. C'est lui qui est responsable de l'émission d'un champ caché "__VIEWSTATE" qui pose bien des problèmes.
Avec ASP.net Ajax, on utilise de plus en plus des UpdatePanel, or le fonctionnement d'un UpdatePanel est exactement le même qu'un postback : le viewstate transite entre les appels ! Dans certains cas cela peut être une cause de problème de performance, il peut alors être intéressant de stocker cet état au niveau du serveur dans la session. De plus, il semble que si l'on utilise massivement le viewstate, le CPU et la mémoire s'affole. Pour sauvegarder l'état de la page dans le session on peut utiliser le SessionPageStatePersister. Deux possibilités :
- Vous disposez d'une classe Page abstraite commune à toutes vos pages (ce que je recommande). Il faut alors surcharger la propriété PageStatePersister de la sorte :
protected override PageStatePersister PageStatePersister
{
get
{
return new SessionPageStatePersister(this.Page);
}
}
- Vous ne disposez pas d'une classe Page abstraite. Il vous faut alors utiliser un Control Adapter :
public class MyPageAdapter : System.Web.UI.Adapters.PageAdapter
{
public override PageStatePersister GetStatePersister()
{
return new SessionPageStatePersister(Page);
}
}
Puis dans votre fichier .browser rajouter votre
ControlAdapter :
<browsers>
<browser refID="Default">
<controlAdapters>
<adapter controlType="System.Web.UI.Page"
adapterType="CS.CSSFriendly.MyPageAdapter" />
Par défaut, le ControlState est encore stocké dans le champ caché __ViewState pour modifier ce comportement il faut modifier le fichier .browser
<browsers>
<browser refID="Default">
<capabilities>
<capability name="requiresControlStateInSession" value="true" />
</capabilities>
Le ControlState est généralement très petit et contient l'état d'un contrôle même si l'on met EnableViewState à false : Control State vs. View State Example (MSDN)
[UPDATE] m'a donné une autre solution pour modifier ce comportement directement au niveau du web.config. Vu ici : ViewState, ControlState et PageStatePersister
<configuration>
<system.web>
<browserCaps>
<case>RequiresControlStateInSession=true</case>
</browserCaps>
</system.web>
</configuration>
[/UPDATE]
Le champ "__ViewState" restera malgré tout toujours présent. Pourquoi ? En fait, le viewstate ne contiendra plus que la clé de l'état dans la session, il pésera moins de 100 octets. Si vous ne voulez plus du champ ViewState vous pouvez toujours modifier ce comportement en surchargeant les méthodes SavePageStateToPersistenceMedium et LoadPageStateToPersistenceMedium.
Attention : Changer ce paramètre n'est pas une choses anodine ! En utilisant le SessionPageStatePersister vous allez considérablement augmenter les ressources utilisé par votre serveur, en effet toutes les requêtes vont alors enregistrer l'état en session, y compris les requêtes qui ne gèrent pas les sessions (bot, refus des cookies, ...) Il est alors bon de bien customiser son fichier .browser pour utiliser le SessionPageStatePersister avec les clients/pages qui nécessitent l'utilisation du SessionStatePersister.
L'avantages d'utiliser un champ caché et au niveau de la gestion de l'historique. En effet lorsque l'on va sur la page précédente, on retrouve le bon viewstate. Pour gérer ce cas là, le SessionPageStatePersister utilise une Queue qui contient par défaut les 9 derniers états en session. Pour modifier cette valeur il faut modifier le web.config de la sorte :
<configuration>
<system.web>
<sessionPageState historySize="9"/>
Remarque : Bien sur, mettre l'état des contrôles sur le serveur est une solution à bien des problèmes, mais il est également nécessaire de désactiver le viewstate là où il n'est pas indispensable. Pour cela il faut bien comprendre le fonctionnement de celui-ci. L'article TRULY Understanding ViewState vous sera très utile.
PS : Je n'ai pas encore utilisé cette solution dans un vrai site web, mais je vais l'utiliser assez rapidement. Si vous avez des retour d'experience, n'hésitez pas à les partager :)
D'autres en parlent :