ViewStates auf dem Server speichern

In einem meiner letzten ASP.NET-Projekte wurden die ViewStates ziemlich exzessiv eingesetzt. ViewStates sind eine tolle Sache, aber wenn viele Daten im ViewState gespeichert werden leidet irgendwann die Performance, da diese in einem hidden-Field ständig mitgeladen werden. Ein Lösungsansatz wäre, den ViewState nicht mehr im Client zu speichern, sondern physikalisch auf dem Server abzulegen. Dabei werden nicht selten 40-60 Kb an Datenvolumen pro Seite eingespart.

Um dies zu erreichen müssen folgende Methoden überschrieben werden:
– LoadPageStateFromPersistenceMedium()
– SavePageStateToPersistenceMedium(object viewState)

LoadPageStateFromPersistenceMedium()
in dieser Methode werden alle gespeicherten Informationen über den Anzeigestatus in das Page-Objekt geladen.

protected override object LoadPageStateFromPersistenceMedium(){
    object oViewState;

    //——————————
    //Laden der Daten
    //——————————

    StreamReader reader = new StreamReader(GenerateFileName());
    string strContent = reader.ReadToEnd();
    reader.Close();

    //——————————
    //Deserialisieren
    //——————————
    LosFormatter oFormatter = new LosFormatter();
    try{
        oViewState = oFormatter.Deserialize(cont);
    }
    catch(Exception ex){
        throw new HttpException("ViewState ist Korrupt!");
    }
    return oViewState;
}

Als erstes wird ein StreamReader() Objekt erstellt. Die Methode GenerateFileName() wird weiter unten vorgestellt. Der Inhalt der jetzt gelesen wird, wird in die Variable strContent gepackt. Dann wird über die Klasse LosFormatter() versucht, den ViewState der aus der Textdatei geliefert wurde zu deserialisieren.

Bis auf den LosFormatter() hier nichts außergewöhnliches. Der LosFormatter() macht nichts anderes als den Anzeigestatus einer Page zu serialisieren oder zu deserialisieren. Daher muss auch nicht näher auf diese Klasse eingegangen werden da die Funktionalität recht einfach ist.

SavePageStateToPersistenceMedium(object viewState)

Hier erfolgt das speichern des Anzeigestatus einer Page.

protected override void SavePageStateToPersistenceMedium(object viewState) {
    //—————————————–
    //Serialisieren und speichern des ViewState
    //—————————————–
    StreamWriter writer = new StreamWriter(GenerateFileName());
    LosFormatter oFormatter = new LosFormatter();
    oFormatter.Serialize(writer, viewState);
    writer.Close();
}

Mittels StreamWriter() und dem LosFormatter() wird nun der ViewState in eine Text-Datei geschrieben.

Alles schön und gut, nur wie ist es jetzt möglich den ViewState explizit einer Seite und einem User zuzuordnen? Relativ einfach. Der Name der Datei ist entscheidend und dieser wird durch eine entsprechende Funktion generiert.

private string GenerateFileName() {
    //—————————————–
    //Erzeugt einen Dateinamen anhand
    //der URL und Session-ID des Clients
    //—————————————–
    string strFilename = this.Request.Url.ToString() + "_" + this.Session.SessionID;
    //—————————————–
    //Herausfiltern nicht erlaubter Zeichen
    //—————————————–
    strFilename = strFilename.Replace(".", "_");
    strFilename = strFilename.Replace(":", "_");
    strFilename = strFilename.Replace("/", "_");
    strFilename = strFilename.Replace("?", "_");
    strFilename = Server.MapPath(strFilename + ".txt");
    return strFilename;
}

Der Dateiname setzt sich aus folgenden Teilen zusammen:

Die URL, damit auch wirklich pro Seite ein eigener ViewState gespeichert werden kann.
Der SessionID des Users, damit der ViewState auch eindeutig zugeordnet werden kann.
Dann werden die Sonderzeichen heraus geparst und schon hat meinem einen eindeutigen Namen der einem User und einer Seite zugeordnet werden kann.

All das sollte reichen damit der ViewState nicht mehr im Client mitgeführt wird. Die Vorteile liegen auf der Hand. Es werden weniger Daten übertragen und der ViewState kann auch von dritten nicht mehr entschlüsselt werden.

Das ganze noch einmal in VB.NET:

Protected Overrides Function LoadPageStateFromPersistenceMedium() As Object

        Dim oViewState As Object
        ‚——————————
        ‚Laden der Daten
        ‚——————————
        Dim reader As New StreamReader(GenerateFileName())
        Dim strContent As String = reader.ReadToEnd()
        reader.Close()

        ‚——————————
        ‚Deserialisieren
        ‚——————————
        Dim oFormatter As New LosFormatter
        Try
            oViewState = form.Deserialize(cont)
        Catch ex As Exception
            Throw New HttpException("ViewState konnte nicht gelesen werden! " & ex.ToString())
        End Try
        Return oViewState
    End Function

Protected Overrides Sub SavePageStateToPersistenceMedium(ByVal viewState As Object)

        ‚————————————————
        ‚Serialisieren und Speichern des ViewStates
        ‚————————————————

        Dim writer As StreamWriter = New StreamWriter(GenerateFileName())
        Dim oFormatter As LosFormatter = New LosFormatter
        oFormatter.Serialize(writer, viewState)
        writer.Close()
    End Sub

    Private Function GenerateFileName() As String
        ‚————————————————
        ‚ Erzeugt einen Dateinamen anhand
        ‚ der URL und Session-ID des Clients
        ‚————————————————
        Dim strFilename As String = Me.Request.Url.ToString() & "_" & Me.Session.SessionID
        ‚————————————————
        ‚ Herausfiltern nicht erlaubter Zeichen
        ‚————————————————
        strFilename = strFilename.Replace(".", "_")
        strFilename = strFilename.Replace(":", "_")
        strFilename = strFilename.Replace("/", "_")
        strFilename = strFilename.Replace("?", "_")
        strFilename = Server.MapPath(strFilename + ".txt")
        Return strFilename
    End Function