Galileo Computing < openbook >
Galileo Computing - Programming the Net
Galileo Computing - Programming the Net


Einstieg in ASP.NET von Matthias Lohrer
Einstieg in ASP.NET
gp Kapitel 11 Der Status von Seiten, Sitzungen und Applikationen
  gp 11.1 Versteckte Felder in Formularen nutzen
  gp 11.2 Der Status einer Seite: Viewstate
    gp 11.2.1 Eigene Daten im Viewstate speichern
    gp 11.2.2 Den Anzeigestatus ein- und ausschalten
  gp 11.3 Der Status einer Sitzung: Session
    gp 11.3.1 Die Klasse HttpSessionState
    gp 11.3.2 Wie ASP.NET die Probleme des klassischen ASP löst
    gp 11.3.3 Übertragung der Session-ID: Cookie oder URL
    gp 11.3.4 Den Sitzungsstatus ein- und ausschalten
    gp 11.3.5 Den Sitzungsstatus konfigurieren
    gp 11.3.6 Session-Ereignisse verwenden
    gp 11.3.7 Objekte mit Session-Scope einbinden
    gp 11.3.8 Instanzen eigener Klassen in den Sitzungsdaten speichern
  gp 11.4 Cookies verwenden
  gp 11.5 Der Status einer Anwendung: Application
    gp 11.5.1 Den Zugriff auf anwendungsweite Daten synchronisieren
    gp 11.5.2 Beschränkungen von Application-Variablen


Galileo Computing

11.3 Der Status einer Sitzung: Session  downtop

Was ist eine Sitzung? Ein Anwender ruft mit seinem Browser eine Website auf. Er sieht sich einige Seiten an, gibt vielleicht irgendwelche Daten ein und verlässt die Website schließlich wieder. Das ist eine Sitzung, auf englisch Session. Aus der Sicht des Anwenders handelt es sich um eine Folge zusammenhängender Seitenaufrufe. Für den Server ist dieser Zusammenhang nicht auf Anhieb ersichtlich. Er wird künstlich durch die Verwaltung von Zustandsinformationen hergestellt.


Galileo Computing

11.3.1 Die Klasse HttpSessionState  downtop

ASP.NET ermöglicht die Sitzungsverfolgung mit Hilfe einer Collection vom Typ HttpSessionState, die das Page-Objekt über seine Eigenschaft Session zur Verfügung stellt. Mit der Abfrage

If Session.IsNewSession

kann ein Skript prüfen, ob mit dem aktuellen Seitenaufruf eine neue Session beginnt, und entsprechend reagieren. Wenn der Anwender seinen Namen beispielsweise in das Eingabefeld txtName eingibt, kann dieser Name in einer Session-Variablen gespeichert werden:

Session("Anwendername") = txtName.value

Damit steht der Name allen anderen Seiten zur Verfügung und kann dort aufgerufen werden, beispielsweise so:

Hallo <% = Session("Anwendername") %>

Tabelle 11.2 listet die verfügbaren Eigenschaften und Methoden von HttpSessionState auf.

Je nachdem, ob ein Seitenaufruf zu einer laufenden Session gehört oder ob eine neue Sitzung beginnt, kann eine Seite ein völlig unterschiedliches Verhalten zeigen. Die Seiten session01.aspx und session02.aspx demonstrieren dieses Verhalten. Die Startseite session01.aspx bietet dem Neueinsteiger eine Anmeldemöglichkeit (siehe Abbildung 11.2). Wenn die Startseite jedoch während einer bereits laufenden Session erneut aufgerufen wird, erscheint eine andere Meldung (siehe Abbildung 11.4). Die Seite session02.aspx wertet ebenfalls eine Session-Variable aus (siehe Abbildung 11.3).

Abbildung
Hier klicken, um das Bild zu Vergrößern

Abbildung 11.2 Weil eine neue Session beginnt, bietet die Startseite eine Anmeldemöglichkeit.

<!-- session01.aspx --> 
<%@ Page Language="VB" Debug="True" Strict="True"  %>
<script runat="server">
Sub Page_Load (ByVal Sender As Object, _
               ByVal E As EventArgs)

   If Session.IsNewSession Then
      neueSession.Visible = true
      bekannteSession.Visible = False
   Else
      neueSession.Visible = False
      bekannteSession.Visible = True   
   End If
   If IsPostBack Then
      Session("Anwendername") = txtName.value
      ausgabe.innerText = "Hallo " & _
                   CStr(Session("Anwendername")) & "!"   End If
End Sub
</script>
<html><head><title>Session-Demo</title></head>
<body><h3>Session-Demo</h3>

<div id="neueSession" runat="server">
<form runat="server" id="myForm">
Ich kenne Sie noch nicht. Wie heißen Sie?
<input type="text" id="txtName" runat="server">
<input type="submit" id="btnOK" runat="server" 
       value="OK" >
<p id="ausgabe" runat="server" />
</form>
</div>

<div id="bekannteSession" runat="server">
<p>Hallo, <% = session("Anwendername") %>, auch mal
wieder auf der Startseite zu Besuch?</p>
</div>

<p><a href="session02.aspx">Zur zweiten Seite</a></p>
</body></html>

Abbildung
Hier klicken, um das Bild zu Vergrößern

Abbildung 11.3 Folgeseiten werten Session-Variablen aus.

session02.aspx wertet eine Session-Variable aus.

<!-- session02.aspx --> 
<%@ Page Language="VB" Debug="True" Strict="True"  %>
<script runat="server">
Sub Page_Load (ByVal Sender As Object, _
               ByVal E As EventArgs)
   ausgabe.innerText = CStr(Session("Anwendername")) _
              & ", Du bist hier auf der zweiten Seite."
End Sub
</script>
<html><head>
<title>Session-Demo, zweite Seite</title></head>
<body><h3>Session-Demo, zweite Seite</h3>
<p id="ausgabe" runat="server" />
<p><a href="session01.aspx">Zur Startseite</a>
</body></html>

Abbildung
Hier klicken, um das Bild zu Vergrößern

Abbildung 11.4 Beim wiederholten Besuch blendet die Startseite die Anmeldemöglichkeit aus.


Name Typ Beschreibung
Eigenschaften
Contents HttpSessionState Existiert nur, um Kompatibilität zu früheren ASP-Versionen zu bieten
Count Integer Anzahl der gespeicherten Session-Variablen
IsCookieless Boolean True, wenn die Session-ID in den URL eingebettet wird, sonst False
IsNewSession Boolean True, wenn die Sitzung mit der aktuellen Anforderung beginnt
IsReadOnly Boolean True, wenn die Sitzung schreibgeschützt ist
Item(String) oder Item(Integer) Object Ruft den Wert einer Session-Variablen über den Bezeichner oder über den numerischen Index auf
Keys NameObjectCollectionBase.KeysCollection Auflistung aller gespeicherten Schlüssel
Mode SessionStateMode Möglichkeiten sind InProc, Off, SQLServer und StateServer
SessionID String Session-ID
StaticObjects HttpStaticObjectsCollection Auflistung von Objekten, die durch <object Runat="Server" Scope="Session"/>-Tags innerhalb von global.asax deklariert werden
Timeout Integer Anzahl der Minuten, nach denen eine Sitzung verfällt
Methoden
Abandon() Bricht die aktuelle Sitzung ab. Nur bei mode="InProc" verfügbar
Add (String, Object) Speichert eine neue Session-Variable
Clear() Löscht alle Session-Variablen
CopyTo(Array, Integer) Kopiert die Session-Variablen in ein eindimensionales Array
GetEnumerator() Enumerator für alle Session-Variablen
Remove(String) Löscht die Session-Variablen mit dem Schlüssel, der im String übergeben wird
RemoveAll() Löscht alle Session-Variablen
RemoveAt(Integer) Löscht die Session-Variable an der angegeben Indexposition

Tabelle 11.2 Mitglieder der Klasse HttpSessionState


Galileo Computing

11.3.2 Wie ASP.NET die Probleme des klassischen ASP löst  downtop

Im Vergleich zum klassischen ASP bietet ASP.NET für die Sitzungsverfolgung eine Reihe neuer Möglichkeiten. Diese lassen sich am besten verstehen, wenn man die Probleme kennt, die das klassische ASP mit der Sitzungsverfolgung hatte. Ein Teil dieser Probleme resultierte aus der Arbeitsweise des Servers. Ein anderer Teil wurde durch die Art der Kommunikation mit dem Client verursacht.

Die Probleme auf der Serverseite resultierten daraus, dass ASP die Session-Variablen ausschließlich im Arbeitsspeicher des Webservers hielt. Daraus ergaben sich zwei Probleme:

1. Wenn die Server-Hard- oder Software abstürzte, waren alle Session-Daten rettungslos verloren. Die Dauerhaftigkeit der Daten war nicht gesichert. 2. Weil die Session-Daten nur im Adressraum einer einzelnen Maschine zur Verfügung standen, konnten Sessions nicht für Webfarmen verwendet werden. In Webfarmen werden mehrere Server zu einem Verbund zusammengeschlossen, um die anfallende Masse von Anfragen besser beantworten zu können.

Das Problem in der Kommunikation mit dem Client bestand darin, dass ASP für die Session-Verwaltung auf Cookies angewiesen war. Wenn nicht alle Firewalls Cookies passieren ließen und nicht alle Browser Cookies unterstützten, scheiterte die ASP-Session-Verwaltung.

ASP.NET löst diese Probleme. Der folgende Abschnitt 11.3.3, Übertragung der Session-ID: Cookie oder URL, demonstriert genauer, wie ASP.NET das Problem mit den Cookies löst. Die Session-ID kann jetzt statt über Cookies auch über den URL transportiert werden. Diese Variante kann weder von Firewalls noch von Browsern behindert werden, denn der URL ist das grundlegende Zahnrad der ganzen Internetmaschinerie.

Für die Lösung der serverseitigen Probleme ermöglicht es ASP.NET, die Session-Daten aus dem Serverprozess auszugliedern und in einem externen Prozess zu speichern. Genauer gesagt, bietet ASP.NET hier zwei Möglichkeiten:

1. Der Sitzungsstatus kann in einem speziellen Statusprozess gespeichert werden, der als NT-Dienst zur Verfügung gestellt wird. Dieser Dienst kann auf dem gleichen Rechner wie der Webserver oder auf einem anderen Rechner laufen, und er kann mehrere Webserver »bedienen«. Vorteil: Nach einem Absturz des Webservers sind die Sitzungs-Informationen noch immer vorhanden. Nachteil: Wenn der Session-Dienst selbst abstürzt, sind die Sitzungsinformationen verloren. 2. Wenn die Sitzungsverwaltung auch einen »Totalschaden« überleben soll, können die Sitzungsdaten dauerhaft in einem SQL Server gespeichert werden.

Beide Methoden sollten wohl überlegt zum Einsatz kommen. Auf der einen Seite gewinnt die Applikation durch die externe Speicherung der Sitzungsinformationen an Stabilität. Andererseits sind Zugriffe auf externe Prozesse stets aufwendiger als prozessinterne Abläufe, so dass mit einer schlechteren Performance zu rechnen ist.

Aus den genannten neuen Möglichkeiten resultieren im Wesentlichen zwei Ergebnisse:

1. Aufgrund der Unabhängigkeit von Browsern und Firewalls ist die Sitzungsverwaltung von ASP.NET universell einsetzbar. 2. Wegen der externen Speicherung der Sitzungsinformationen ist ASP.NET gut skalierbar.

Der Abschnitt 11.3.5, Den Sitzungsstatus konfigurieren, informiert detailliert über die Möglichkeit, den Sitzungsstatus zu konfigurieren.


Galileo Computing

11.3.3 Übertragung der Session-ID: Cookie oder URL  downtop

Woher weiß der Server, zu welcher Session ein Seitenaufruf gehört? Jede Sitzung wird über eine eindeutige Session-ID identifiziert. Diese Session-ID muss der Browser stets dem Server übermitteln. Dafür bietet ASP.NET zwei Möglichkeiten:

gp  Als Standard verwendet ASP.NET für die Übermittlung der Session-Id Cookies. Das war auch beim klassischen ASP bislang die einzige Möglichkeit.
gp  Neu hinzugekommen ist die Möglichkeit, die Session-ID in den URL einzubetten.

Die verwendete Methode stellen Sie mit Hilfe der Datei web.config ein. Wenn Sie Cookies vermeiden möchten, nehmen Sie in web.config einen entsprechenden Eintrag vor:

<configuration>
  <system.web>
      <sessionState cookieless="true" />
      ...
    </system.web>
</configuration>

Wenn Sie diese Änderung eingeben und anschließend die Beispielseite session01.aspx erneut aufrufen, erkennen Sie den Unterschied. Abbildung 11.5 zeigt ein Beispiel. ASP.NET fügt bei jedem Seitenaufruf die Session-ID als Pfadbezeichner vor den eigentlichen Seitennamen ein. Die URL-Zeile gewinnt damit beispielsweise diese Form:

http://localhost/ASPdotNETBuch/(yut0peqswve15a45hx1mqp55)/Listings/session01.aspx

Abbildung
Hier klicken, um das Bild zu Vergrößern

Abbildung 11.5 Für Sessions ist ASP.NET nicht auf Cookies angewiesen. Die Session-ID kann alternativ in die URL-Zeile eingebettet werden.

Auf diese Weise können Sie Sitzungen auch dann verfolgen, wenn ein Browser Cookies deaktiviert hat oder wenn eine Firewall keine Cookies passieren lässt.

Wenn Sie keine Cookies verwenden, müssen Sie bei Links darauf achten, dass Sie innerhalb Ihrer Applikation nur relative Links verwenden. Diesen Link setzt ASP.NET korrekt um:

<a href="session02.aspx">

ASP.NET kann hier die Session-ID in den URL einbetten und im Browser erscheint korrekt ein Sprung zu

http://localhost/ASPdotNETBuch/(5kx35545ahmr1datkr0aba45)/Listings/session02.aspx

Dieser Link funktioniert nicht korrekt:

<a href="http://localhost/
         ASPdotNETBuch/Listings/session02.aspx">

In diesen hart codierten Link kann ASP.NET die benötigte Session-ID nicht einbetten. Die benötigten Session-Variablen stünden anschließend nicht mehr zur Verfügung.


Tipp   Wenn Sie dennoch komplette Pfade angeben möchten, bietet die Methode ApplyAppPathModifier der HttpResponse-Klasse die Möglichkeit, den kompletten Aufruf wenigstens manuell zu erzeugen.

tmp = Response.ApplyAppPathModifier("session01.aspx") 

Dieser Funktionsaufruf gibt beispielsweise folgenden String zurück:

/ASPdotNETBuch/(jbbp0cmp22bkrkbn0kdw5n3a)/Listings/
session01.aspx

Anschließend können Sie diesen String in dynamisch erzeugten Links verwenden.


Achtung   ASP.NET verwendet für die Sitzungsverfolgung entweder die Cookie- oder die URL-Methode. Wenn Ihre Applikation für die Übermittlung der Session-ID Cookies verwendet, schaltet ASP.NET nicht automatisch auf die URL-Einbettungsmethode um, wenn ein Browser keine Cookies akzeptiert. In diesem Fall würde die Applikation nicht funktionieren.

Die URL-Methode wird nur dann verwendet, wenn Sie das in der web.config-Datei ausdrücklich angeben. Wenn also die Möglichkeit besteht, dass irgendein Aufrufer mit Cookies nicht zurechtkommt, dann müssen Sie von vornherein Cookies ausschalten und generell mit der URL-Methode arbeiten.


Galileo Computing

11.3.4 Den Sitzungsstatus ein- und ausschalten  downtop

Wenn Ihre Webapplikation keine Sessions benötigt, dann sollten Sie die Unterstützung für Sessions ausdrücklich abschalten, denn damit schonen Sie die Ressourcen des Servers. In einem anderen Fall benötigen vielleicht nur einzelne Seiten den Zugriff auf Sitzungsdaten. Für andere Seiten sind die Sitzungsdaten dagegen unerheblich. Die Unterstützung für Sessions können Sie an verschiedenen Stellen ein- oder ausschalten. In den Dateien machine.config und web.config ist es zunächst möglich, eine Voreinstellung für alle Seiten vorzunehmen. Der folgende Eintrag in der Datei web.config aktiviert Sessions für alle Seiten als Default-Einstellung:

<configuration>
   <system.web>
      <pages enableSessionState="true" />
         ...
   </system.web>
</configuration>

Das Attribut enableSessionState verfügt über drei mögliche Werte: true, false und ReadOnly. Der Wert in der Datei web.config kann von jeder aspx-Seite in der Page-Direktive überschrieben werden. Wenn eine bestimmte Seite keine Sessions unterstützen soll, muss die Page-Direktive einen entsprechenden Eintrag enthalten:

<%@ Page EnableSessionState="false" %>

Wenn während einer laufenden Sitzung auf diese Seite zugegriffen wird, dann wird die Sitzung nicht ungültig, aber innerhalb dieser Seite kann nicht auf Session-Variablen zugegriffen werden. Entsprechend verhält sich die Option enableSessionState="ReadOnly". In diesem Fall können Session-Variablen gelesen, aber nicht neu definiert werden.

Die Beispielseiten session03.aspx und session04.aspx verdeutlichen, wie Seiten mit und ohne Sessions nebeneinander existieren können. session03.aspx setzt EnableSessionState="false". session04.aspx setzt EnableSessionState="true". Beim Aufruf von session03.aspx wird keine Session angelegt. Beim Sprung zu session04.aspx wird eine Session angelegt. Beim Rücksprung zu session03.aspx bleibt die Session erhalten, die Seite kann aber die Session-Daten nicht auswerten. Wenn man bei diesem Beispiel die Session-ID über den URL transportieren lässt, wird deutlich, wann keine Session existiert, wann sie angelegt wird und ob sie erhalten bleibt. Abbildung 11.6 bis Abbildung 11.8 zeigen die Schritte.

Abbildung
Hier klicken, um das Bild zu Vergrößern

Abbildung 11.6 Beim ersten Aufruf der Seite ohne Session-Unterstützung wird keine Session angelegt.

<!-- session03.aspx --> 
<%@ Page Language="VB" Debug="True" Strict="True" 
         EnableSessionState="False" %>
<html><head><title>Keine Session</title></head><body>
<h3>Das ist eine Seite ohne Session-Unterstützung</h3>
<a href="session04.aspx">Zur Seite 
mit Session-Unterstützung</a>
</body></html>

Abbildung
Hier klicken, um das Bild zu Vergrößern

Abbildung 11.7 Beim Sprung zur Seite mit Session-Unterstützung wird eine neue Session angelegt, was an dem URL mit Session-ID zu sehen ist.

<!-- session04.aspx --> 
<%@ Page Language="VB" Debug="True" Strict="True" 
         EnableSessionState="true" %>
<html><head><title>Mit Session</title></head><body>
<h3>Das ist eine Seite mit Session-Unterstützung</h3>
<a href="session03.aspx">Zur Seite 
ohne Session-Unterstützung</a></body></html>

Abbildung
Hier klicken, um das Bild zu Vergrößern

Abbildung 11.8 Beim Rücksprung zur Seite ohne Session-Unterstützung bleibt die Session dennoch erhalten.


Galileo Computing

11.3.5 Den Sitzungsstatus konfigurieren  downtop

In der Datei web.config stellen Sie mit dem Element sessionState ein, wie der Sitzungsstatus gespeichert werden soll. Das Element sessionState hat diese Optionen:

<sessionState mode="Off|Inproc|StateServer|SQLServer"
            cookieless="true|false"
            timeout="Anzahl Minuten"
            stateConnectionString="tcpip=server:port"
            sqlConnectionString="sql connection string" 
/>

Keine Unterstützung von Sessions

Wenn Ihre Anwendung keine Sessions unterstützen soll, geben Sie dem Attribut mode den Wert Off.

Sessions im eigenen Serverprozess verwalten

Wenn Sessions innerhalb des Webserverprozesses verwaltet werden sollen, geben Sie dem Attribut mode den Wert Inproc.

Einen separaten ASP.NET-Statusdienst verwenden

Wenn Sie für die Statusverwaltung einen eigenen NT-Dienst betreiben, dann ist mode="StateServer" der passende Wert. In diesem Fall müssen Sie außerdem im Attribut stateConnectionString die IP-Adresse und Port-Nummer des Dienstes angeben. Ein Beispiel:

<sessionState
   mode= "StateServer"
   stateConnectionString="tcpip=127.0.0.1:42424" />

Den ASP.NET-Statusdienst starten und beenden Sie über Start Einstellungen Systemsteuerung Verwaltung Dienste (siehe Abbildung 11.9).

Den Sitzungsstatus in einem SQL Server speichern

Die High-End-Lösung für die Sitzungsverwaltung ist die Speicherung der Sitzungsdaten in einem SQL Server. Das Attribut mode benötigt in diesem Fall den Wert SQLServer. Dem Attribut sqlConnectionString müssen Sie die benötigte Verbindungszeichenfolge zuweisen. Ein Beispiel:

<sessionState
   mode= "SQLServer"
   sqlConnectionString=
   "data source=127.0.0.1;user id=sa; password=geheim" 
/>

Abbildung
Hier klicken, um das Bild zu Vergrößern

Abbildung 11.9 Der ASP.NET-Statusdienst verwaltet Sessions in einem NT-Dienst, der unabhängig vom Webserver läuft.

In Ihrem SQL Server müssen Sie die entsprechenden Vorbereitungen treffen. Die benötigten Datenbanken, Tabellen und Stored Procedures legt das Skript InstallSqlState.sql für Sie an, das Sie standardmäßig im Pfad C:\WINNT\Microsoft.NET\Framework\v1.0.3705 finden.

Die Attribute cookieless und timeout bieten weitere Konfigurationsmöglichkeiten. Mit cookieless="true" werden die Session-IDs nicht über Cookies, sondern über den URL transportiert.

Mit dem timeout-Attribut stellen Sie ein, nach wie vielen Minuten eine Session verfällt.


Galileo Computing

11.3.6 Session-Ereignisse verwenden  downtop

Eine beginnende Sitzung löst das Ereignis Session_OnStart() aus. Wenn eine Sitzung endet, wird das Ereignis Session_OnEnd() ausgelöst. Die Klasse SessionStateModule stellt diese Ereignisse bereit. Die Ereignisprozeduren gehören in die Datei global.asax, die im Stammverzeichnis der Anwendung liegt. Hier können Sie Startwerte initialisieren und nach dem Sitzungsende Aufräumarbeiten durchführen.

Ein Beispiel soll das Verfahren illustrieren. Jede Session soll sich ihren eigenen Startzeitpunkt merken. Die Datei global.asax muss beispielsweise diese Form haben:

<script language="VB" runat="server">
Sub Session_OnStart()
   Session("Startzeitpunkt") = DateTime.Now
End Sub
</script>

Mit dem Aufruf von session05.aspx beginnt eine neue Session.

<!-- session05.aspx --> 
<%@ Page Language="VB" Debug="True" Strict="True"  %>
<script runat="server">
Sub Page_Load (ByVal Sender As Object, _
               ByVal E As EventArgs)
   ausgabe.innerText = "Startpunkt der Session: " & _
                       CStr(Session("Startzeitpunkt"))
   ausgabe2.innerText = "Jetzt ist es: " & DateTime.Now
End Sub
</script>
<html><head><title>Session_OnStart-Demo, Seite 1</title></head>
<body><h3>Session_OnStart-Demo, Seite 1</h3>
<p runat="server" id="ausgabe" />
<p runat="server" id="ausgabe2" />
<a href="session06.aspx">Zur Seite 2</a>
</body></html>

Die Seite gibt den Startzeitpunkt der Session und die aktuelle Zeit aus. Ein Link führt zur Datei session06.aspx. Diese Datei enthält die gleichen Ausgaben mit einer aktualisierten Angabe der aktuellen Zeit. Indem Sie zwischen session05.aspx und session06.aspx hin- und herspringen (Abbildung 11.10 und Abbildung 11.11), können Sie beobachten, dass beide Dateien stets die gleiche Angabe für den Startzeitpunkt und eine aktualisierte Zeitangabe ausgeben.

Abbildung
Hier klicken, um das Bild zu Vergrößern

Abbildung 11.10 Über die Ereignisprozedur Session_OnStart merkt sich jede Session ihren Startzeitpunkt.

Abbildung
Hier klicken, um das Bild zu Vergrößern

Abbildung 11.11 Über die Session-Variable können alle Seiten auf den Startzeitpunkt zugreifen.

<!-- session06.aspx --> 
<%@ Page Language="VB" Debug="True" Strict="True"  %>
<script runat="server">
Sub Page_Load (ByVal Sender As Object, _
               ByVal E As EventArgs)
   ausgabe.innerText = "Startpunkt der Session: " & _
                       CStr(Session("Startzeitpunkt"))
   ausgabe2.innerText = "Jetzt ist es: " & DateTime.Now
End Sub
</script>
<html><head><title>Session_OnStart-Demo, Seite 2</title></head>
<body><h3>Session_OnStart-Demo, Seite 2</h3>
<p runat="server" id="ausgabe" />
<p runat="server" id="ausgabe2" />
<a href="session05.aspx">Zur Seite 1</a>
</body></html>

Galileo Computing

11.3.7 Objekte mit Session-Scope einbinden  downtop

Sie können für eine Session auch Klasseninstanzen erstellen, auf die Sie von allen Seiten aus zugreifen können. Dafür stellt die Datei global.asax das Element object zur Verfügung. Die allgemeine Syntax lautet:

<object id="myID"
        runat="server"
        scope="session"
        class="Name der Klasse"
/>

Auch hierzu wieder ein Beispiel. In einem Objekt vom Typ StringBuilder möchten Sie die Abfolge der Seiten speichern, die ein Anwender beim Besuch Ihrer Website aufruft.


Achtung   Bevor Sie dieses Beispiel in der Praxis einsetzen, sollten Sie sich eingehend über die datenschutzrechtlichen Aspekte dieses Themas informieren. Sie verstoßen gegen das Datenschutzrecht, wenn Sie personenbezogene Daten mit Aufzeichnungen zur zurückgelegten Datenspur kombinieren. Solange die Aufzeichnungen anonym bleiben und der Anwender außerdem mit der anonymen Aufzeichnung seiner Datenspur einverstanden ist, ist das Verfahren rechtlich unbedenklich. Im konkreten Fall sollten Sie jedoch die datenschutzrechtlichen Aspekte Ihrer Anwendung von einem Experten prüfen lassen.

Nun aber zur technischen Umsetzung der geplanten Aufzeichnung. Sie erzeugen für jede neue Session eine Instanz vom Typ StringBuilder, indem Sie in der Datei global.asax diesen Eintrag vornehmen:

<object id="mySB"
        runat="server"
        scope="session"
        class="System.Text.StringBuilder"
/>

Anschließend steht die Variable mySB auf allen Seiten als Eigenschaft zur Verfügung. Die Seiten session07.aspx und session08.aspx fügen die aktuelle Seite jeweils dem StringBuilder-Objekt hinzu:

mySB.append (Request.FilePath & "<br>")

Den bislang zurückgelegten Pfad geben Sie im Browser aus:

ausgabe.innerHTML = mySB.toString()

Wenn Sie zwischen session07.aspx und session08.aspx hin- und herwechseln und einige Male einen Reload einer Seite ausführen, können Sie beobachten, wie die Liste der besuchten Seiten allmählich anwächst (siehe Abbildung 11.12).

<!-- session07.aspx --> 
<%@ Page Language="VB" Debug="True" Strict="True"  %>
<script runat="server">
Sub Page_Load (ByVal Sender As Object, _
               ByVal E As EventArgs)
   mySB.append (Request.FilePath & "<br>")
   ausgabe.innerHTML = mySB.toString()
End Sub
</script>
<html><head><title>
Demo zur Klasseninstanz mit Session-Scope, Seite 1
</title></head>
<body><h3>Demo zur Klasseninstanz mit Session-Scope, Seite 1</h3>
<p>Diese Session hat diesen Pfad zurückgelegt:</p> 
<p runat="server" id="ausgabe" />
<a href="session08.aspx">Zur Seite 2</a>
</body></html>

Das Beispiel können Sie abrunden, indem Sie in global.asax noch die Ereignisprozedur Session_OnEnd ergänzen. Beim Abschluss einer Session könnte die aufgezeichnete Datenspur beispielsweise gespeichert werden.

Abbildung
Hier klicken, um das Bild zu Vergrößern

Abbildung 11.12 In einem StringBuilder-Objekt speichert jede Session den zurückgelegten Datenpfad. Beachten Sie den Datenschutz!


Galileo Computing

11.3.8 Instanzen eigener Klassen in den Sitzungsdaten speichern  toptop

Sie können nicht nur Instanzen von Klassen aus dem .NET Framework, sondern auch Instanzen selbst geschriebener Klassen für die Sitzungsverfolgung nutzen. Das Beispiel aus dem vorigen Abschnitt lässt sich entsprechend erweitern, indem Sie Ihre eigene Klasse Besuchsverlauf definieren.


Achtung   Wenn Sie eine Klasse entwerfen mit dem Ziel, sie bei der Sitzungsverfolgung zu verwenden, sollten Sie darauf achten, dass die Klasse serialisierbar ist. Der Grund dafür liegt in der Art und Weise, wie die Session-Daten gespeichert werden. Solange die Session-Daten im gleichen Prozessraum wie Ihre Applikation gehalten werden, müssen die Session-Daten nicht unbedingt serialisierbar sein. Das ist der Fall, wenn der sessionState-Eintrag in der web.config das Attribut mode="InProc" enthält.

Sobald die Sitzungsdaten jedoch mit einem eigenen ASP.NET-Sitzungsdienst (mode="StateServer") oder mit einem SQL Server (mode="SQLServer") verwaltet werden, müssen die Daten kontinuierlich zum Verwaltungsdienst hin- und von dort aus wieder zurücktransportiert werden. Das geht nur, wenn diese Daten serialisierbar sind. Zeiger und Datei-Handles sind Beispiele nicht serialisierbarer Daten.

Das folgende Beispiel demonstriert die Verwendung einer selbst definierten Klasse bei der Sitzungsverwaltung. Die Datei Besuch.vb definiert die Klasse Besuchsverlauf. Da nur das Prinzip gezeigt werden soll, ist die Klasse denkbar schlicht. Sie stellt lediglich ein StringBuilder-Objekt und ein DateTime-Objekt als öffentliche Eigenschaften zur Verfügung.

' Besuch.vb
Imports System
Public Class Besuchsverlauf
   Public mySB As New System.Text.StringBuilder()
   Public myStart As System.DateTime    
End Class

Kompilieren Sie diese Klasse in eine dll-Datei. Verwenden Sie dazu diesen Aufruf:

vbc /target:library Besuch.vb

Der Compiler erzeugt die Datei Besuch.dll. Diese Datei kopieren Sie in das /bin-Verzeichnis unterhalb des Stammverzeichnisses Ihrer Anwendung.

Erstellen Sie im Stammverzeichnis Ihrer Anwendung eine Datei global.asax mit diesem Inhalt:

<% @Assembly name="Besuch" %>
<object id="myVerlauf"
        runat="server"
        scope="session"
        class="Besuchsverlauf" 
/>

<script language="VB" runat="server">
Sub Session_OnStart()
   myVerlauf.myStart = System.DateTime.Now
End Sub
</script>

Mit der @Assembly-Direktive machen Sie Ihre Assembly Besuch bekannt. Mit dem object-Element erzeugen Sie für jede neue Session eine Instanz der Klasse Besuchsverlauf. Beim Start einer neuen Session wird außerdem die myStart-Eigenschaft der Besuchsverlauf-Klasse initialisiert.

Nun benötigen Sie noch ein oder zwei Seiten, die Ihr Besuchsverlauf-Objekt nutzen. session09.aspx merkt sich den bisherigen Verlauf, zeigt ihn an und bietet außerdem die Möglichkeit, die Session ausdrücklich zu beenden.

<!-- session09.aspx --> 
<%@ Page Language="VB" Debug="True" Strict="True"  %>
<script runat="server">
Sub Page_Load (ByVal Sender As Object, _
               ByVal E As EventArgs)
   If IsPostBack Then
      Session.Abandon()
      Response.Redirect _
               ("http://localhost/ASPdotNETBuch/
                 Listings/session09.aspx")
   Else 
      myVerlauf.mySB.append _
                          (Request.FilePath & "<br>")
      ausgabe.innerHTML = myVerlauf.mySB.toString()   
   End If

End Sub
</script>
<html><head><title>
Demo zu: Instanz einer selbst definierten Klasse mit 
Session-Scope, Seite 1
</title></head>
<body><h3>Demo zu: Instanz einer selbst definierten 
Klasse mit Session-Scope, Seite 1</h3>
<p>Diese Session hat diesen Pfad zurückgelegt:</p> 
<p runat="server" id="ausgabe" />
<a href="session10.aspx">Zur Seite 2</a>
<form runat="server">
<input type="submit" value="Session beenden" 
       runat="server" >
</form></body></html>

session10.aspx speichert den Verlauf und zeigt ihn an. Mit Links können Sie zwischen diesen beiden Dateien hin- und herspringen.

<!-- session10.aspx --> 
<%@ Page Language="VB" Debug="True" Strict="True"  %>
<script runat="server">
Sub Page_Load (ByVal Sender As Object, _
               ByVal E As EventArgs)
   myVerlauf.mySB.append (Request.FilePath & "<br>")
   ausgabe.innerHTML = myVerlauf.mySB.toString()
End Sub
</script>
<html><head><title>
Demo zu: Instanz einer selbst definierten Klasse 
mit Session-Scope, Seite 2
</title></head>
<body><h3>Demo zu: Instanz einer selbst definierten 
Klasse mit Session-Scope, Seite 2</h3>
<p>Diese Session hat diesen Pfad zurückgelegt:</p> 
<p runat="server" id="ausgabe" />
<a href="session09.aspx">Zur Seite 1</a>
</body></html>

Und nun müssen Sie noch darauf achten, dass die Datei web.config bei sessionState zunächst den Eintrag mode="InProc" trägt.

<configuration>
   <system.web>
      <pages enableSessionState="true" />
      <sessionState cookieless="true" 
                    mode="InProc" />
   </system.web>
</configuration>

Rufen Sie session09.aspx im Browser auf, klicken Sie den Link Zur Seite 2 an und springen Sie einige Male zwischen den beiden Seiten hin und her. Die Liste der besuchten Seiten wird immer länger (siehe Abbildung 11.13).

Schließlich klicken Sie auf der Seite session09.aspx auf die Schaltfläche Session beenden. Anschließend wird die laufende Sitzung beendet und die Seite wird neu geladen, wodurch eine neue Session beginnt.

So weit, so gut. Und jetzt möchten Sie für die Sitzungsverfolgung den separaten ASP.NET-Statusdienst nutzen.

Abbildung
Hier klicken, um das Bild zu Vergrößern

Abbildung 11.13 Hier speichern Sie den Sitzungsverlauf mit Hilfe einer selbst definierten Klasse.

Überprüfen Sie über Start Einstellungen Systemsteuerung Verwaltung Dienste, ob der ASP.NET-Statusdienst bereits läuft (siehe Abbildung 11.9). Stellen Sie dann die Datei web.config auf mode="StateServer" um:

<configuration>
   <system.web>
      <pages enableSessionState="true" />
      <sessionState cookieless="true" 
          mode="StateServer"
          stateConnectionString="tcpip=127.0.0.1:42424"
/>
   </system.web>
</configuration>

Wenn Sie anschließend die Seite session09.aspx neu aufrufen, erscheint die in Abbildung 11.14 gezeigte Fehlermeldung.

Die Bedeutung der Fehlermeldung ist nicht auf Anhieb erkennbar. Was soll es heißen, wenn da behauptet wird Der Typ Besuchsverlauf ... ist als serialisierbar markiert? Wir haben die Klasse doch gar nicht ausdrücklich als serialisierbar markiert?! Verstehen Sie es einfach so: Die Klasse sollte entsprechend gekennzeichnet sein.

Abbildung
Hier klicken, um das Bild zu Vergrößern

Abbildung 11.14 Wenn der ASP.NET-Statusdienst oder ein SQL Server die Sitzungsdaten verwaltet, müssen Session-Objekte serialisierbar sein.

Um den Fehler zu beheben, ist nur eine kleine Korrektur im Quellcode der Klasse nötig. Sie müssen vor der Klassendeklaration das Attribut Serializable angeben:

' Besuch.vb
Imports System
<Serializable()> Public Class Besuchsverlauf
   Public mySB As New System.Text.StringBuilder()
   Public myStart As System.DateTime    
End Class

Kompilieren Sie die Klasse neu. Kopieren Sie Besuch.dll wieder in das /bin-Verzeichnis. Anschließend funktionieren die Seiten wie gewünscht.

Es würde zu weit führen, an dieser Stelle das Konzept der Serialisierbarkeit im Detail zu erläutern. Genauere Informationen dazu finden Sie etwa bei Andreas Kühnel: VB.NET.

Vorsicht vor Session_OnEnd!

Nachdem Sie diese Klippe gut umschifft haben, werden Sie vielleicht auf die Idee kommen, der Klasse eine Methode hinzuzufügen, die die gesammelten Daten speichert. Diese Methode soll stets beim Beenden einer Session aufgerufen werden. Dafür ließe sich die Ereignisprozedur Session_OnEnd einsetzen.

Gesagt, getan. In der Datei Besuch.vb ergänzen Sie innerhalb der Klasse Besuchsverlauf eine Methode Speichern, etwa so:

' Besuch.vb
Imports System
Imports System.IO
Imports System.Runtime.Serialization

<Serializable()> Public Class Besuchsverlauf
   Public mySB As New System.Text.StringBuilder()
   Public myStart As System.DateTime
   Public Sub Speichern (ByVal inSessionID As String)
      Dim datnam As String
      datnam = "C:\session" & inSessionID
      Dim strom As New StreamWriter(datnam)
      strom.WriteLine (CStr(myStart))
      strom.Write (mySB.toString())
      strom.Close()
   End Sub       
End Class

Diese Datei kompilieren Sie und kopieren sie in das /bin-Verzeichnis.

In der Datei global.asax ergänzen Sie die Ereignisprozedur Session_OnEnd:

Sub Session_OnEnd()
   myVerlauf.Speichern(Session.SessionID)
End Sub

Sie rufen session09.aspx im Browser auf, wechseln zu session10.aspx, kehren zu session09.aspx zurück und klicken dann die Schaltfläche Session beenden an. Anschließend suchen Sie im Explorer auf dem Laufwerk c: nach einer Datei, die mit session... anfängt, aber weit und breit ist nichts davon zu sehen. Wie das? Seltsamerweise ist auch kein Fehler zu bemerken.


Achtung   Des Rätsels Lösung: Das Ereignis Session_OnEnd tritt nur ein, wenn für die Sitzungsverwaltung die Option mode="InProc" gewählt wurde. Wenn Sie den separaten ASP.NET-Statusdienst oder einen SQL Server für die Sitzungsverwaltung verwenden, tritt das Session_OnEnd-Ereignis gar nicht ein. So teilt es auch die Online-Dokumentation etwas versteckt mit. Mit anderen Worten: Das Ereignis Session_OnEnd ist nicht skalierbar.

Probieren Sie es aus. Ändern Sie den Eintrag in der web.config wieder zurück zu mode="InProc":

<sessionState cookieless="true" 
       mode="InProc" 
       stateConnectionString="tcpip=127.0.0.1:42424" />

Das stateConnectionString-Attribut können Sie trotzdem stehen lassen, es stört nicht. Wieder ein Test: hin- und herwechseln und Session beenden anklicken. Und siehe da: Jetzt befindet sich auf Laufwerk c: die ersehnte Datei, z. B. mit dem Namen sessionlxobau45ymidcw45yhg4lx55. Wenn Sie die Datei öffnen, finden Sie darin zunächst eine Datumsangabe mit Uhrzeit und im Anschluss daran die Liste der besuchten Seiten, die jeweils mit einem <br> voneinander getrennt sind (siehe Abbildung 11.15).

Abbildung
Hier klicken, um das Bild zu Vergrößern

Abbildung 11.15 Beim Beenden einer Sitzung speichern Sie den Verlauf der besuchten Seiten ab.

Damit stellt sich die Frage, wie Sie die Verlaufsdaten sichern können, wenn Sie die Sitzungsdaten extern verwalten. Im Prinzip bleibt Ihnen dann nichts anderes übrig, als den zurückgelegten Datenpfad bei jeder neu aufgerufenen Seite sofort zu speichern.

  

Einstieg in VB.NET

VB.NET

Einstieg in C#

Visual C#

VB.NET und Datenbanken

Einstieg in XML




Copyright © Galileo Press GmbH 2003
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Galileo Computing]

Galileo Press GmbH, Gartenstraße 24, 53229 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de