Bonjour,
Aujourd'hui en relation avec mon article sur la compression du Cache et de la Session, je vous propose de voir la compression du ViewState.
Le ViewState est un élément qui peut être très coûteux pour les performance d'ouverture d'une page lorsque l'on utilise des Contrôles qui utilisent beaucoup le ViewState (GridView, DataList etc..)
Pour ce faire nous allons créer une classe permettant de gérer cette compression nommée ViewStateCompression qui sera héritée de PageStatePersister ainsi qu'une classe de base pour nos page web afin d'utiliser cette implémentation de compression de ViewState.
Dans notre classe ViewStateCompression qui hérite de PageStatePersister nous allons simplement implémenter deux méthodes afin de compresser et décompresser notre ViewState.
- public override void Load()
- public override void Save()
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.IO;
using System.IO.Compression;
namespace ViewStateCompression
{
public class ViewStateCompression : PageStatePersister
{
#region Private Fields
private LosFormatter _stateFormatter;
#endregion
#region Protected Properties
protected LosFormatter StateFormatter
{
get
{
if (_stateFormatter == null)
_stateFormatter = new LosFormatter();
return _stateFormatter;
}
}
#endregion
#region Constructors
public ViewStateCompression(Page page)
: base(page)
{
}
#endregion
#region Public PageStatePersister Overrides Methods
public override void Load()
{
byte[] bts = Convert.FromBase64String(base.Page.Request.Form["__VSTATE"]);
byte[] uncompressed = Decompress(bts);
Pair p = (Pair)StateFormatter.Deserialize(Convert.ToBase64String(uncompressed));
base.ViewState = p.First;
base.ControlState = p.Second;
}
public override void Save()
{
TextWriter sw = new StringWriter();
StateFormatter.Serialize(sw, new Pair(base.ViewState, base.ControlState));
byte[] bts = Convert.FromBase64String(sw.ToString());
byte[] compressed = Compress(bts);
ScriptManager sm = ScriptManager.GetCurrent(base.Page);
if (sm != null)
{
ScriptManager.RegisterHiddenField(base.Page, "__VSTATE",
Convert.ToBase64String(compressed));
}
else
{
Page.ClientScript.RegisterHiddenField("__VSTATE",
Convert.ToBase64String(compressed));
}
}
#endregion
#region Private Static Methods
private byte[] Compress(byte[] data)
{
using (MemoryStream output = new MemoryStream())
{
using (DeflateStream def = new DeflateStream(output, CompressionMode.Compress))
{
def.Write(data, 0, data.Length);
}
return output.ToArray();
}
}
private byte[] Decompress(byte[] data)
{
using (MemoryStream input = new MemoryStream())
{
input.Write(data, 0, data.Length);
input.Position = 0;
using (DeflateStream def = new DeflateStream(input, CompressionMode.Decompress))
{
using (MemoryStream output = new MemoryStream())
{
byte[] buff = new byte[64];
int read = -1;
read = def.Read(buff, 0, buff.Length);
while (read > 0)
{
output.Write(buff, 0, read);
read = def.Read(buff, 0, buff.Length);
}
def.Close();
return output.ToArray();
}
}
}
}
#endregion
}
}
Rien de très compliqué dans cette classe, nous utilisons pour la compression la classe
DeflateStream suite au retours de performances que j'avais décris dans mon
article précédent.
Vous remarquez cependant dans la méthode Save() le code suivant
ScriptManager sm = ScriptManager.GetCurrent(base.Page);
if (sm != null)
{
ScriptManager.RegisterHiddenField(base.Page, "__VSTATE", Convert.ToBase64String(compressed));
}
else
{
Page.ClientScript.RegisterHiddenField("__VSTATE", Convert.ToBase64String(compressed));
}
Il s'agit en effet de la prise en charge d'Ajax dans le cas ou nous avons un ScriptManager présent dans nos pages.
Passons maintenant a notre classe de base pour nos pages web.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
namespace ViewStateCompression
{
public class BasePage : Page
{
#region Private Fields
private ViewStateCompression _persister = null;
#endregion
#region Constructors
public BasePage()
{
_persister = new ViewStateCompression(this);
}
#endregion
#region Protected Overrides Properties
protected override PageStatePersister PageStatePersister
{
get
{
return _persister;
}
}
#endregion
}
}
Nous y instancions donc notre classe ViewStateCompression et nous modifions la propriété PageStatePersister afin d'utiliser notre propre Persister.
Maintenant vous allez me dire "oui mais quels en sont les résultats?"
Et bien les voici par un exemple simple. Nous allons créer une Web Form qui hérite de notre BasePage et y ajouter deux boutons
- Création d'un objet "lourd" et enregistrement dans le ViewState
- Lecteur du ViewState
protected void Populate_Click(object sender, EventArgs e)
{
byte[] bts = new byte[20000];
for (int i = 0; i < bts.Length; i++)
{
bts[i] = 0x45;
}
ViewState["Data"] = bts;
}
et
protected void ReadViewState_Click(object sender, EventArgs e)
{
foreach (byte b in ViewState["Data"] as Byte[])
{
Response.Write(b.ToString());
}
}
Nous obtenons les résultats suivants.
Sans ViewStateCompression : Fichier de 56 KB
Avec ViewStateCompression : Fichier de 4 KB
Sympa non? :)
yield return this;
Views(1023)

