Peter Bucher - Mein Experiment, meine Spielereien, meine Welt...   ·   Stefan Falz   ·   Jürgen Gutsch   ·   Golo Roden   ·   ASP.NET Zone   ·   Microsoft ASP.NET
Willkommen bei ASP.NET Zone. Anmelden | Registrieren | Hilfe

Facade Pattern und ein Anwendungsbeispiel: SessionFacade

Vor einiger Zeit habe ich über das Strategy-Pattern und Singleton-Pattern geschrieben, dort sind auch Links zu der Definition und den Grundlagen von Designpatterns enthalten.

Die Definition des Facade Patterns (Oder Fassade Musters) liest sich wie folgt:

Das Facade-Muster bietet eine vereinheitlichte Schnittstelle für einen Satz von Schnittstellen eines Basis-
systems. Die Fassade definiert eine hochstufigere Schnittstelle, die die Verwendung des Basissystems vereinfacht.

Mit anderen Worten geht es darum den Zugriff auf ein System innerhalb einer Klasse zu kapseln und anhand einer neuen (vereinfachten / erweiterten) Schnittstelle zur Verfügung zu stellen.
Der SessionState wird im Arbeitsspeicher des Servers gehalten, darum lässt sich jeder - noch so komplexe - Typ darin speichern.
Über den Sinn und Unsinn der Verwendung des SessionStates, sowie dessen Funktionsweise habe ich schon einmal geschrieben.

Der SessionState ist nicht...

  • ...typsicher -> Allle Objekte werden von einer Auflistung von Objekten gehalten und zurückgegeben (Typ: object)
  • ...sicher gegen Tippfehler-> Beim Speichern kann der Name für eine Session bspw. "UserId" lauten und beim Aufrufen "UsrId".
  • ...kontextsicher -> Es gibt keine Garantie das beim Hinzufügen eines Objektes der Name irgendwo im Code noch nie verwendet wurd

Ein erster Schritt zur Besserung wäre die Einführung von Konstanten, die beim Abrufen der Session-Variablen benutzt werden, bspw.:



private const string _user = "User";

... (Irgendwo weiter im Code)

User user = Session[_user];
Session[_user] = <Wert>;

Wenn diese Konstanten global gehalten werden, ist man schon mal das Tippfehler-Problem los.
Jedoch ist die Vorgehensweise immer noch nicht typsicher und bietet auch keine anderen Vorteile.

Keine Typsicherheit bedeutet unter anderem, dass beim setzen der Variable bspw. ein String übergeben wird und beim auslesen versucht wird, diesen in ein DateTime zu casten - was einen Laufzeitfehler zur Folge hätte.

Für diese Situation ist das Facade-Pattern bestens geeignet.
Das Basissystem und dessen Schnittstellen stellt in diesem Fall die HttpSessionState-Klasse von ASP.NET dar.
Die Facade implementiert alles um die oben genannten Nachteile des direkten Zugriffs über die HttpSessionState-Klasse nach aussen hin zu beseitigen.

Folgend eine Implementation des SessionFacade bei der eine Session-Variable typisiert zur Verfügung gestellt wird:



/// <summary>
/// Stellt eine Fassade für den typsicheren Zugriff auf den HttpSessionState dar.
/// </summary>
public static class SessionFacade
{
    #region Private Constants

    private const string _user = "User";

    #endregion

    #region Private Helpermethods

    /// <summary>
    /// Gibt einen typisierten Sessionwert zurück
    /// </summary>
    /// <typeparam name="T">Typ des Sessionwertes</typeparam>
    /// <param name="name">Der Schlüsselname des Sessionwertes</param>
    /// <returns></returns>
    private static T GetSessionValue<T>(string name) {
        object value = HttpContext.Current.Session[name];
        if (value != null)
            return (T)value;

        return default(T);
    }

    /// <summary>
    /// Setzt einen Sessionwert
    /// </summary>
    /// <param name="name">Der Schlüsselname des Sessionwertes</param>
    /// <param name="value">Zu speicherndes Objekt</param>
    private static void SetSessionValue(string name, object value) {
        if (value == null)
            HttpContext.Current.Session.Remove(name);

        HttpContext.Current.Session[name] = value;
    }

    #endregion

    #region Public Properties

    /// <summary>
    /// Setzt den aktuellen Benutzer oder gibt in zurück
    /// </summary>
    public static User User {
        get {
            return GetSessionValue<User>(_user);
        }

        set {
            SetSessionValue(_user, value);
        }
    }

    #endregion
}

Benutzt werden kann die Facaden-Klasse z.B. so:



// Abrufen
User currentUser = SessionFacade.User;
if(currentUser != null)
    // Mach was mit dem User

// Setzen
SessionFacade.User = user;

Die Facade-Klasse und dessen Methoden sind statisch gehalten um einen einfachen Zugriff zu ermöglichen.
Durch die Eigenschaft "HttpContext.Current.Session" ist sichergestellt dass das SessionState-Objekt auch threadsicher ist.

Die Hilfsmethoden GetSessionValue und SetSessionValue kapseln den Zugriff auf den SessionState, geben bei Nichtvorhandensein eines Wertes jeweils Standardwerte zurück (DateTime.MinValue, Guid.Empty, 0, ...), ersparen Schreibarbeit und Casting in den Eigenschaften.
Die Eigenschaften können auch noch weitere Logik enthalten um bspw. auf leere oder ungültige Werte anders zu reagieren.

Im Normalfall gibt es einige Session-Variablen die nach aussen zur Verfügung gestellt werden, im Beispiel habe ich es bei einer belassen.
Das Hinzufügen von weiteren Zugriffsmöglichkeiten auf Session-Variablen erfolgt durch die Deklaration einer Konstante und
einer korrespondierenden Eigenschaft dazu.

Die Vorteile liegen klar auf der Hand:

  • Starke Typisierung beim Ein- und Auslesen mithilfe der Facade
  • Dadurch ist kein Casting mehr nötig
  • Schreibfehler beim Namen für die Session-Variablen sind - bei sorgfältiger Erstellung der Facade - praktisch ausgeschlossen
  • Der Zugriff erfolgt durch eine einzelne Klasse
  • Die konkrete Implementation wird durch die Facade vor dem Cientcode (Bspw. Codebehind einer Seite) versteckt und...
  • ...kann intern verändert und erweitert werden, ohne den Clientcode zwingend anpassen zu müssen

Viele von euch werden jetzt vielleicht denken:

  • Wozu der ganze Aufwand?
  • Für jede Session-Variable muss ich die SessionFacade-Klasse ändern?!

Nun, der Aufwand ist im Vergleich zum Nutzen und Komfort (Siehe Vorteile) extrem klein.
Ausserdem animiert eine solche Facade auch zur sinnvollen Nutzung des SessionStates, da immer zweimal überlegt wird, ob eine Session-Variable verwendet werden soll oder nicht.

Veröffentlicht Dienstag, 9. Dezember 2008 21:50 von Peter Bucher

Kommentare

# re: Facade Pattern und ein Anwendungsbeispiel: SessionFacade

Ach Mensch Peter

public static User User { <snip>

Das kannst du besser.

Und komm jetzt bitte nicht mit: "...ist ja nur ein Beispiel." Neulinge sehen dein Beispiel und übernehmen ungefragt solche Unarten. Bitte sei wieder Vorbild :)

Servus,

Klaus

Mittwoch, 10. Dezember 2008 06:52 by klaus_b

# re: Facade Pattern und ein Anwendungsbeispiel: SessionFacade

Hallo Klaus

Ist ja nett das du kritisiert, aber wie wäre es mit einem Verbesserungsvorschlag? ;-)

Static ist in diesem Zusammenhang überhaupt kein Problem (Threading), ausser du möchtest die Facade testen können.

Dafür würde sich IoC und eine Mock-Implementierung anbieten.

Und diesem Thema könnte man sich wiederum gleich mit ein paar neuen Artikeln zuwenden, wodurch die Komplexität steigt.

Aber ich bin ganz Ohr :-)

Grüsse Peter

Mittwoch, 10. Dezember 2008 09:18 by Peter Bucher

# re: Facade Pattern und ein Anwendungsbeispiel: SessionFacade

Hallo Peter,

das static ist vollkommen ok und absolut in Ordnung. Hast du ja schon selber ausreichend begründet.

Was mich stört ist User User, also das Typ und Member den gleichen Namen haben. Dein Beispiel ist IMO nicht falsch, da ja die Benutzung eines Types als Membernamen legitim ist; nur halt nicht sehr schön. Wenn ich mich recht erinnere wird dieses Thema irgenwo in den Namens-Konventionen geregelt. Hier mal ein verwantes Thema: http://msdn.microsoft.com/de-de/library/bb531486.aspx

Entschuldige bitte, dass ich mich im ersten Kommentar nicht klar ausgedrückt habe.

Als Verbesserungsvorschlag vieleicht:

public static User CurrentUser { <snip>

BTW: Mir viel dieser "Ausrutscher" nur auf, weil ich gerade in einem Projekt einiges mit Linq2Sql-Klassen zu tun habe die von anderen Personen generiert wurden. Ich verwende diese Klassen und stolpere andauern über Warnungen weil Typ und Klassennamen gleich lauten ala:

public class Log Log { <snip>

Servus,

Klaus

Mittwoch, 10. Dezember 2008 10:28 by klaus_b

# re: Facade Pattern und ein Anwendungsbeispiel: SessionFacade

Laut "Framework Design Guidelines", 2. Auflage, Seite 70:

"Consider giving a property the same name as its type."

Als Beispiel wird eine Eigenschaft Color gezeigt, die auf eine Enumeration Color zugreift - also hat man Color Color.

Finde ich persönlich ehrlich gesagt auch nicht verkehrt ...

Mittwoch, 10. Dezember 2008 12:39 by Golo Roden

# re: Facade Pattern und ein Anwendungsbeispiel: SessionFacade

Hallo Klaus

Ach so hast du das gemein ;-)

> Wenn ich mich recht erinnere wird dieses Thema irgenwo

Wo denn?

User sagt ja das wesentliche aus und bei Microsoft selber scheint es auch geteilte Meinungen zu geben.

Namenskonflikte gibt es keine, von daher sehe ich da kein Problem.

Gruss Peter

Mittwoch, 10. Dezember 2008 12:41 by Peter Bucher

# re: Facade Pattern und ein Anwendungsbeispiel: SessionFacade

Noch als Anmerkung ... die von Klaus erwähnte FxCop-Regel bezieht sich auf => Parameter <=, nicht auf Eigenschaften ...

Mittwoch, 10. Dezember 2008 13:27 by Golo Roden

# Typkonvertierung vereinfacht, oder: <Value>.To<Type>();

*Update Im Zuge meiner Arbeit an der SessionFacade ging mir das Licht auf, dass wir es im Optimalfall

Dienstag, 16. Dezember 2008 20:36 by Peter Bucher
Anonyme Kommentare sind nicht zugelassen