Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
von Scott Mitchell
Die einfachste Zwischenspeicherungsstrategie besteht darin, zwischengespeicherte Daten nach einem bestimmten Zeitraum ablaufen zu lassen. Dieser einfache Ansatz bedeutet jedoch, dass die zwischengespeicherten Daten keine Zuordnung zu ihrer zugrunde liegenden Datenquelle haben, was dazu führt, dass veraltete Daten zu lange behalten werden oder aktuelle Daten zu schnell ablaufen. Ein besserer Ansatz besteht darin, die SqlCacheDependency-Klasse zu verwenden, sodass Daten erst zwischengespeichert werden, wenn die zugrunde liegenden Daten in der SQL-Datenbank geändert wurden. In diesem Lernprogramm wird gezeigt, wie das geht.
Einleitung
Die Zwischenspeichertechniken, die in den Zwischenspeichern von Daten mit ObjectDataSource und Zwischenspeichern von Daten in den Architekturtutorials untersucht wurden, verwendeten eine zeitbasierte Ablaufsteuerung, um die Daten nach einem bestimmten Zeitraum aus dem Cache zu entfernen. Dieser Ansatz ist die einfachste Methode, um die Leistungsgewinne des Zwischenspeicherns mit der Veraltetkeit von Daten zu ausgleichen. Wenn Sie einen Zeitablauf von x Sekunden wählen, genießt ein Seitenentwickler die Leistungsvorteile des Zwischenspeicherns nur für x Sekunden, kann aber sicher sein, dass deren Daten nie länger als maximal x Sekunden veraltet sind. Für statische Daten kann x selbstverständlich auf die Dauer der Webanwendung ausgedehnt werden, wie im Tutorial 'Daten zwischenspeichern beim Starten der Anwendung' untersucht wurde.
Bei der Zwischenspeicherung von Datenbankdaten wird häufig ein zeitbasierter Ablauf aufgrund seiner einfachen Handhabung gewählt, ist aber oft keine ausreichende Lösung. Im Idealfall bleiben die Datenbankdaten zwischengespeichert, bis die zugrunde liegenden Daten in der Datenbank geändert wurden; nur dann würde der Cache gelöscht. Dieser Ansatz maximiert die Leistungsvorteile des Zwischenspeicherns und minimiert die Dauer veralteter Daten. Um diese Vorteile nutzen zu können, muss es jedoch ein System geben, das weiß, wann die zugrunde liegenden Datenbankdaten geändert wurden und die entsprechenden Elemente aus dem Cache entfernt werden. Vor ASP.NET 2.0 waren Seitenentwickler für die Implementierung dieses Systems verantwortlich.
ASP.NET 2.0 stellt eine SqlCacheDependency
Klasse und die erforderliche Infrastruktur bereit, um festzustellen, wann eine Änderung in der Datenbank stattgefunden hat, damit die entsprechenden zwischengespeicherten Elemente entfernt werden können. Es gibt zwei Techniken zum Bestimmen, wann sich die zugrunde liegenden Daten geändert haben: Benachrichtigung und Abfrage. Nachdem wir die Unterschiede zwischen Benachrichtigung und Abruf erörtert haben, erstellen wir die Infrastruktur, die erforderlich ist, um das Abfragen zu unterstützen, und untersuchen dann, wie die SqlCacheDependency
Klasse in deklarativen und programmgesteuerten Szenarien verwendet wird.
Grundlegendes zu Benachrichtigungen und Umfragen
Es gibt zwei Techniken, die verwendet werden können, um zu bestimmen, wann die Daten in einer Datenbank geändert wurden: Benachrichtigung und Abruf. Mit Benachrichtigung benachrichtigt die Datenbank automatisch die ASP.NET Laufzeit, wenn die Ergebnisse einer bestimmten Abfrage seit der letzten Ausführung der Abfrage geändert wurden, an dem an diesem Punkt die zwischengespeicherten Elemente, die der Abfrage zugeordnet sind, entfernt werden. Bei der Abfrage verwaltet der Datenbankserver Informationen darüber, wann bestimmte Tabellen zuletzt aktualisiert wurden. Die ASP.NET Laufzeit fragt die Datenbank regelmäßig ab, um zu überprüfen, welche Tabellen seit der Eingabe in den Cache geändert wurden. Bei den Tabellen, deren Daten geändert wurden, werden die zugeordneten Cacheelemente entfernt.
Die Benachrichtigungsoption erfordert weniger Setup als das Abfragen und ist detaillierter, weil sie Änderungen auf der Abfrageebene und nicht auf der Tabellenebene nachverfolgt. Leider sind Benachrichtigungen nur in den vollständigen Editionen von Microsoft SQL Server 2005 (d. h. die Nicht-Express-Editionen) verfügbar. Die Abfrageoption kann jedoch für alle Versionen von Microsoft SQL Server von 7.0 bis 2005 verwendet werden. Da diese Lernprogramme die Express-Edition von SQL Server 2005 verwenden, legen wir den Fokus auf das Einrichten und Verwenden der Abfrageoption. Weitere Informationen zu SQL Server 2005-Benachrichtigungsfunktionen finden Sie im Abschnitt "Weiteres Lesen" am Ende dieses Lernprogramms.
Bei der Abfrage muss die Datenbank so konfiguriert werden, dass sie eine Tabelle mit dem Namen AspNet_SqlCacheTablesForChangeNotification
enthält, die drei Spalten enthält : tableName
, notificationCreated
und changeId
. Diese Tabelle enthält eine Zeile für jede Tabelle mit Daten, die in einer SQL-Cacheabhängigkeit in der Webanwendung verwendet werden müssen. Die tableName
Spalte gibt den Namen der Tabelle an, während notificationCreated
das Datum und die Uhrzeit angibt, zu der die Zeile der Tabelle hinzugefügt wurde. Die changeId
Spalte ist vom Typ int
und hat den Anfangswert 0. Der Wert wird mit jeder Änderung der Tabelle erhöht.
Zusätzlich zur AspNet_SqlCacheTablesForChangeNotification
Tabelle muss die Datenbank auch Trigger für jede der Tabellen enthalten, die in einer SQL-Cacheabhängigkeit angezeigt werden können. Diese Trigger werden ausgeführt, wenn eine Zeile eingefügt, aktualisiert oder gelöscht wird und der Wert der Tabelle changeId
in AspNet_SqlCacheTablesForChangeNotification
erhöht wird.
Die ASP.NET Laufzeit verfolgt den aktuellen changeId
für eine Tabelle beim Zwischenspeichern von Daten mithilfe eines SqlCacheDependency
Objekts. Die Datenbank wird in regelmäßigen Abständen überprüft, und alle SqlCacheDependency
Objekte, deren changeId
Wert sich vom Wert in der Datenbank unterscheidet, werden entfernt, da ein unterschiedlicher changeId
Wert darauf hinweist, dass seit der Zwischenspeicherung der Daten eine Änderung an der Tabelle aufgetreten ist.
Schritt 1: Erkunden desaspnet_regsql.exe
Befehlszeilenprogramms
Mit dem Polling-Ansatz muss die Datenbank so eingerichtet werden, dass sie die oben beschriebenen Infrastruktur enthält: eine vordefinierte Tabelle (AspNet_SqlCacheTablesForChangeNotification
), eine Handvoll gespeicherter Prozeduren und Trigger für jede der Tabellen, die in SQL-Cacheabhängigkeiten in der Webanwendung verwendet werden können. Diese Tabellen, gespeicherten Prozeduren und Trigger können über das Befehlszeilenprogramm aspnet_regsql.exe
erstellt werden, das sich im $WINDOWS$\Microsoft.NET\Framework\version
Ordner befindet. Um die Tabelle und die AspNet_SqlCacheTablesForChangeNotification
zugehörigen gespeicherten Prozeduren zu erstellen, führen Sie folgendes über die Befehlszeile aus:
/* For SQL Server authentication... */
aspnet_regsql.exe -S server -U user -P password -d database -ed
/* For Windows Authentication... */
aspnet_regsql.exe -S server -E -d database -ed
Hinweis
Um diese Befehle auszuführen, muss sich der angegebene Datenbank-Login in den db_securityadmin
und db_ddladmin
Rollen befinden.
Um beispielsweise die Infrastruktur für die Abfrage zu einer Microsoft SQL Server-Datenbank mit dem Namen pubs
auf einem Datenbankserver namens ScottsServer
unter Verwendung der Windows-Authentifizierung hinzuzufügen, navigieren Sie zum entsprechenden Verzeichnis und geben Sie in der Befehlszeile Folgendes ein:
aspnet_regsql.exe -S ScottsServer -E -d pubs -ed
Nachdem die Infrastruktur auf Datenbankebene hinzugefügt wurde, müssen wir die Trigger zu den Tabellen hinzufügen, die in SQL-Cacheabhängigkeiten verwendet werden. Verwenden Sie das aspnet_regsql.exe
Befehlszeilenprogramm erneut, geben Sie jedoch den Tabellennamen mithilfe des -t
Schalter an und verwenden statt des Schalters -ed
-et
, zum Beispiel:
/* For SQL Server authentication... */
aspnet_regsql.exe -S <i>server</i>
-U <i>user</i> -P <i>password</i> -d <i>database</i> -t <i>tableName</i> -et
/* For Windows Authentication... */
aspnet_regsql.exe -S <i>server</i>
-E -d <i>database</i> -t <i>tableName</i> -et
Verwenden Sie Folgendes, um die Auslöser zu den authors
und titles
Tabellen in der pubs
Datenbank ScottsServer
hinzuzufügen:
aspnet_regsql.exe -S ScottsServer -E -d pubs -t authors -et
aspnet_regsql.exe -S ScottsServer -E -d pubs -t titles -et
Fügen Sie in diesem Lernprogramm die Trigger zu den Products
, Categories
und Suppliers
Tabellen hinzu. In Schritt 3 werden wir die spezielle Befehlszeilensyntax betrachten.
Schritt 2: Verweisen auf eine Microsoft SQL Server 2005 Express Edition-Datenbank inApp_Data
Für das aspnet_regsql.exe
Befehlszeilenprogramm ist der Datenbank- und Servername erforderlich, um die erforderliche Abrufinfrastruktur hinzuzufügen. Aber was ist der Datenbank- und Servername für eine Microsoft SQL Server 2005 Express-Datenbank, die sich im App_Data
Ordner befindet? Anstatt zu ermitteln, was die Datenbank- und Servernamen sind, habe ich festgestellt, dass der einfachste Ansatz darin besteht, die Datenbank an die localhost\SQLExpress
Datenbankinstanz anzufügen und die Daten mithilfe von SQL Server Management Studio umzubenennen. Wenn Sie eine der Vollversionen von SQL Server 2005 auf Ihrem Computer installiert haben, ist SQL Server Management Studio wahrscheinlich bereits auf Ihrem Computer installiert. Wenn Sie nur über die Express-Edition verfügen, können Sie das kostenlose Microsoft SQL Server Management Studio herunterladen.
Schließen Sie Zunächst Visual Studio. Öffnen Sie als Nächstes SQL Server Management Studio, und wählen Sie die Verbindung mit dem Server mithilfe der localhost\SQLExpress
Windows-Authentifizierung aus.
Abbildung 1: Anfügen an den localhost\SQLExpress
Server
Nach dem Herstellen einer Verbindung mit dem Server zeigt Management Studio den Server an und verfügt über Unterordner für die Datenbanken, die Sicherheit usw. Klicken Sie mit der rechten Maustaste auf den Ordner "Datenbanken", und wählen Sie die Option "Anfügen" aus. Dadurch wird das Dialogfeld "Datenbanken anfügen" angezeigt (siehe Abbildung 2). Klicken Sie auf die Schaltfläche "Hinzufügen", und wählen Sie den NORTHWND.MDF
Datenbankordner in Ihrem Webanwendungsordner App_Data
aus.
Abbildung 2: Anfügen der NORTHWND.MDF
Datenbank aus dem App_Data
Ordner (Klicken Sie hier, um das Bild in voller Größe anzuzeigen)
Dadurch wird die Datenbank dem Ordner "Datenbanken" hinzugefügt. Der Datenbankname kann der vollständige Pfad zu der Datenbankdatei oder der vollständige Pfad sein, der mit einer GUID vorangestellt ist. Um zu vermeiden, dass Sie diesen langen Datenbanknamen eingeben müssen, wenn Sie das Befehlszeilentool aspnet_regsql.exe verwenden, benennen Sie die Datenbank in einen benutzerfreundlicheren Namen um, indem Sie mit der rechten Maustaste auf die Gerade angefügte Datenbank klicken und "Umbenennen" auswählen. Ich habe meine Datenbank in DataTutorials umbenannt.
Abbildung 3: Umbenennen der angefügten Datenbank in einen Human-Friendly-Namen.
Schritt 3: Hinzufügen der Abfrageinfrastruktur zur Northwind-Datenbank
Jetzt, da wir die NORTHWND.MDF
Datenbank aus dem App_Data
Ordner angefügt haben, sind wir bereit, die Polling-Infrastruktur hinzuzufügen. Wenn Sie die Datenbank in DataTutorials umbenannt haben, führen Sie die folgenden vier Befehle aus:
aspnet_regsql.exe -S localhost\SQLExpress -E -d DataTutorials -ed
aspnet_regsql.exe -S localhost\SQLExpress -E -d DataTutorials -t Products -et
aspnet_regsql.exe -S localhost\SQLExpress -E -d DataTutorials -t Categories -et
aspnet_regsql.exe -S localhost\SQLExpress -E -d DataTutorials -t Suppliers -et
Klicken Sie nach dem Ausführen dieser vier Befehle mit der rechten Maustaste auf den Datenbanknamen in Management Studio, wechseln Sie zum Untermenü "Aufgaben", und wählen Sie "Trennen" aus. Schließen Sie dann Management Studio, und öffnen Sie Visual Studio erneut.
Nachdem Visual Studio erneut geöffnet wurde, führen Sie einen Drilldown in die Datenbank über den Server-Explorer durch. Beachten Sie die neue Tabelle (AspNet_SqlCacheTablesForChangeNotification
), die neuen gespeicherten Prozeduren und die Trigger für die Products
, Categories
und Suppliers
Tabellen.
Abbildung 4: Die Datenbank enthält jetzt die erforderliche Abfrageinfrastruktur.
Schritt 4: Konfigurieren des Polling-Dienstes
Nach dem Erstellen der erforderlichen Tabellen, Trigger und gespeicherten Prozeduren in der Datenbank besteht der letzte Schritt darin, den Abrufdienst zu konfigurieren, der durch Web.config
Angabe der zu verwendenden Datenbanken und der Abrufhäufigkeit in Millisekunden erfolgt. Im folgenden Markup wird die Northwind-Datenbank jede Sekunde abgefragt.
<?xml version="1.0"?>
<configuration>
<connectionStrings>
<add name="NORTHWNDConnectionString" connectionString=
"Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|\NORTHWND.MDF;
Integrated Security=True;User Instance=True"
providerName="System.Data.SqlClient"/>
</connectionStrings>
<system.web>
...
<!-- Configure the polling service used for SQL cache dependencies -->
<caching>
<sqlCacheDependency enabled="true" pollTime="1000" >
<databases>
<add name="NorthwindDB"
connectionStringName="NORTHWNDConnectionString" />
</databases>
</sqlCacheDependency>
</caching>
</system.web>
</configuration>
Der name
Wert im <add>
Element ( NorthwindDB ) ordnet einen lesbaren Namen einer bestimmten Datenbank zu. Beim Arbeiten mit SQL-Cacheabhängigkeiten müssen wir auf den hier definierten Datenbanknamen sowie auf die Tabelle verweisen, auf der die zwischengespeicherten Daten basieren. In Schritt 6 erfahren Sie, wie Sie mithilfe der SqlCacheDependency
Klasse SQL-Cacheabhängigkeiten programmgesteuert mit zwischengespeicherten Daten verknüpfen.
Nachdem eine SQL-Cacheabhängigkeit eingerichtet wurde, stellt das Abrufsystem alle <databases>
Millisekunden eine Verbindung zu den in den pollTime
-Elementen definierten Datenbanken her und führt die AspNet_SqlCachePollingStoredProcedure
-gespeicherte Prozedur aus. Diese gespeicherte Prozedur, die in Schritt 3 mithilfe des aspnet_regsql.exe
-Befehlszeilentools hinzugefügt wurde, gibt die Werte von tableName
und changeId
für jeden Datensatz in AspNet_SqlCacheTablesForChangeNotification
zurück. Veraltete SQL-Cacheabhängigkeiten werden aus dem Cache entfernt.
Die pollTime
Einstellung führt zu einem Kompromiss zwischen Leistung und Veraltetkeit von Daten. Ein kleiner pollTime
Wert erhöht die Anzahl der Anforderungen an die Datenbank, entfernt aber schneller veraltete Daten aus dem Cache. Ein größerer pollTime
Wert reduziert die Anzahl der Datenbankanforderungen, erhöht jedoch die Verzögerung zwischen dem Ändern der Back-End-Daten und dem Löschen der zugehörigen Cacheelemente. Glücklicherweise führt die Datenbankanforderung eine einfache gespeicherte Prozedur aus, die nur wenige Zeilen aus einer einfachen, einfachen Tabelle zurückgibt. Experimentieren Sie jedoch mit unterschiedlichen pollTime
Werten, um ein ideales Gleichgewicht zwischen Datenbankzugriff und Datenhäufigkeit für Ihre Anwendung zu finden. Der kleinste zulässige pollTime
Wert ist 500.
Hinweis
Im obigen Beispiel wird ein einzelner pollTime
Wert im <sqlCacheDependency>
Element bereitgestellt, sie können jedoch optional den pollTime
Wert im <add>
Element angeben. Dies ist nützlich, wenn Sie mehrere Datenbanken angegeben haben und die Abrufhäufigkeit pro Datenbank anpassen möchten.
Schritt 5: Deklaratives Arbeiten mit SQL-Cacheabhängigkeiten
In den Schritten 1 bis 4 haben wir uns angesehen, wie die erforderliche Datenbankinfrastruktur eingerichtet und das Abrufsystem konfiguriert wird. Mit dieser Infrastruktur können wir nun elemente zum Datencache mit einer zugeordneten SQL-Cacheabhängigkeit mithilfe von programmgesteuerten oder deklarativen Techniken hinzufügen. In diesem Schritt untersuchen wir, wie Sie deklarativ mit SQL-Cacheabhängigkeiten arbeiten. In Schritt 6 betrachten wir den programmgesteuerten Ansatz.
Im Lernprogramm zur Zwischenspeicherung von Daten mit der ObjectDataSource wurden die deklarativen Caching-Funktionen der ObjectDataSource behandelt. Durch einfaches Festlegen der EnableCaching
Eigenschaft auf True
und die CacheDuration
Eigenschaft auf ein bestimmtes Zeitintervall speichert ObjectDataSource automatisch die daten, die vom zugrunde liegenden Objekt für das angegebene Intervall zurückgegeben werden. Die ObjectDataSource kann auch eine oder mehrere SQL-Cacheabhängigkeiten verwenden.
Um die Verwendung von SQL-Cacheabhängigkeiten deklarativ zu veranschaulichen, öffnen Sie die SqlCacheDependencies.aspx
Seite im Caching
Ordner, und ziehen Sie eine GridView aus der Toolbox auf den Designer. Setzen Sie die GridView auf ID
und wählen Sie im Smarttag die Option, sie an eine neue ObjectDataSource namens ProductsDeclarative
zu binden.
Abbildung 5: Erstellen einer neuen ObjectDataSource namens ProductsDataSourceDeclarative
(Klicken Sie hier, um das Bild in voller Größe anzuzeigen)
Konfigurieren Sie die ObjectDataSource so, dass sie die ProductsBLL
Klasse verwendet, und legen Sie die Dropdownliste auf der Registerkarte SELECT auf GetProducts()
. Wählen Sie auf der Registerkarte UPDATE die UpdateProduct
Überladung mit drei Eingabeparametern aus : productName
, unitPrice
und productID
. Legen Sie die Dropdownlisten auf (Keine) in den Registerkarten EINFÜGEN und LÖSCHEN fest.
Abbildung 6: Verwenden der UpdateProduct-Überladung mit drei Eingabeparametern (Klicken, um das Bild in voller Größe anzuzeigen)
Abbildung 7: Setzen Sie die Drop-Down-Liste auf (Keine) für die Registerkarten Einfügen und Löschen (Klicken Sie, um das Bild in voller Größe anzuzeigen)
Nach Abschluss des Assistenten zum Konfigurieren von Datenquellen erstellt Visual Studio BoundFields und CheckBoxFields im GridView für jedes Datenfeld. Entfernen Sie alle Felder außer ProductName
, CategoryName
und UnitPrice
, und formatieren Sie diese Felder, wie Sie sehen. Aktivieren Sie im Smart-Tag "GridView" die Kontrollkästchen "Paging aktivieren", "Sortierung aktivieren" und "Bearbeitung aktivieren". Visual Studio legt die ObjectDataSource s-Eigenschaft OldValuesParameterFormatString
auf original_{0}
. Damit das Bearbeitungsfeature von GridView ordnungsgemäß funktioniert, entfernen Sie diese Eigenschaft entweder vollständig aus der deklarativen Syntax, oder legen Sie sie auf den Standardwert zurück. {0}
Fügen Sie schließlich ein Label-Websteuerelement oberhalb der GridView hinzu, und legen Sie dessen ID
Eigenschaft auf ODSEvents
und seine EnableViewState
Eigenschaft auf False
. Nachdem Sie diese Änderungen vorgenommen haben, sollte das deklarative Markup der Seite wie folgt aussehen. Beachten Sie, dass ich eine Reihe von ästhetischen Anpassungen an die GridView-Felder vorgenommen habe, die nicht erforderlich sind, um die SQL-Cacheabhängigkeitsfunktionalität zu veranschaulichen.
<asp:Label ID="ODSEvents" runat="server" EnableViewState="False" />
<asp:GridView ID="ProductsDeclarative" runat="server"
AutoGenerateColumns="False" DataKeyNames="ProductID"
DataSourceID="ProductsDataSourceDeclarative"
AllowPaging="True" AllowSorting="True">
<Columns>
<asp:CommandField ShowEditButton="True" />
<asp:TemplateField HeaderText="Product" SortExpression="ProductName">
<EditItemTemplate>
<asp:TextBox ID="ProductName" runat="server"
Text='<%# Bind("ProductName") %>' />
<asp:RequiredFieldValidator ID="RequiredFieldValidator1"
ControlToValidate="ProductName" Display="Dynamic"
ErrorMessage="You must provide a name for the product."
SetFocusOnError="True"
runat="server">*</asp:RequiredFieldValidator>
</EditItemTemplate>
<ItemTemplate>
<asp:Label ID="Label2" runat="server"
Text='<%# Bind("ProductName") %>' />
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField DataField="CategoryName" HeaderText="Category"
ReadOnly="True" SortExpression="CategoryName" />
<asp:TemplateField HeaderText="Price" SortExpression="UnitPrice">
<EditItemTemplate>
$<asp:TextBox ID="UnitPrice" runat="server" Columns="8"
Text='<%# Bind("UnitPrice", "{0:N2}") %>'></asp:TextBox>
<asp:CompareValidator ID="CompareValidator1" runat="server"
ControlToValidate="UnitPrice"
ErrorMessage="You must enter a valid currency value with
no currency symbols. Also, the value must be greater than
or equal to zero."
Operator="GreaterThanEqual" SetFocusOnError="True"
Type="Currency" Display="Dynamic"
ValueToCompare="0">*</asp:CompareValidator>
</EditItemTemplate>
<ItemStyle HorizontalAlign="Right" />
<ItemTemplate>
<asp:Label ID="Label1" runat="server"
Text='<%# Bind("UnitPrice", "{0:c}") %>' />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
<asp:ObjectDataSource ID="ProductsDataSourceDeclarative" runat="server"
SelectMethod="GetProducts" TypeName="ProductsBLL"
UpdateMethod="UpdateProduct">
<UpdateParameters>
<asp:Parameter Name="productName" Type="String" />
<asp:Parameter Name="unitPrice" Type="Decimal" />
<asp:Parameter Name="productID" Type="Int32" />
</UpdateParameters>
</asp:ObjectDataSource>
Erstellen Sie als Nächstes einen Ereignishandler für das ObjectDataSource-Ereignis Selecting
, und fügen Sie darin den folgenden Code hinzu:
Protected Sub ProductsDataSourceDeclarative_Selecting _
(sender As Object, e As ObjectDataSourceSelectingEventArgs) _
Handles ProductsDataSourceDeclarative.Selecting
ODSEvents.Text = "-- Selecting event fired"
End Sub
Erinnern Sie sich daran, dass das ObjectDataSource-Ereignis Selecting
nur ausgelöst wird, wenn Daten aus dem zugrunde liegenden Objekt abgerufen werden. Wenn die ObjectDataSource auf die Daten aus dem eigenen Cache zugreift, wird dieses Ereignis nicht ausgelöst.
Besuchen Sie diese Seite nun über einen Browser. Da wir noch keine Zwischenspeicherung implementiert haben, sollte die Seite bei jedem Wechsel, beim Sortieren oder Bearbeiten des Rasters den Text "Auswahlereignis ausgelöst" anzeigen, wie in Abbildung 8 dargestellt.
Abbildung 8: Das ObjectDataSource-Ereignis Selecting
wird bei jeder Seiten-, Bearbeitungs- oder Sortierungsansicht ausgelöst (Klicken Sie, um das Bild in voller Größe anzuzeigen)
Wie wir im Lernprogramm zum Zwischenspeichern von Daten mit dem ObjectDataSource gesehen haben, bewirkt das Festlegen der Eigenschaft EnableCaching
auf True
, dass die ObjectDataSource ihre Daten für die Dauer zwischenspeichert, die durch ihre CacheDuration
-Eigenschaft angegeben wird. Die ObjectDataSource verfügt auch über eine SqlCacheDependency
Eigenschaft, die eine oder mehrere SQL-Cacheabhängigkeiten zu den zwischengespeicherten Daten mithilfe des Musters hinzufügt:
databaseName1:tableName1;databaseName2:tableName2;...
Dabei ist "databaseName " der Name der Datenbank, wie im name
Attribut des <add>
Elements in Web.config
angegeben, und "tableName " ist der Name der Datenbanktabelle. Wenn Sie z. B. eine ObjectDataSource erstellen möchten, die Daten unbegrenzt basierend auf einer SQL-Cacheabhängigkeit mit der Northwind-Tabelle Products
zwischenspeichert, legen Sie die ObjectDataSource-Eigenschaft EnableCaching
auf True
und deren SqlCacheDependency
Eigenschaft auf "NorthwindDB:Products" fest.
Hinweis
Sie können eine SQL-Cacheabhängigkeit und einen zeitbasierten Ablauf verwenden, indem Sie EnableCaching
auf True
, CacheDuration
auf das Zeitintervall und SqlCacheDependency
auf die Datenbank- und Tabellennamen festlegen. Die ObjectDataSource löscht die Daten, wenn der zeitbasierte Ablauf erreicht wird oder wenn das Abrufsystem darauf hinweist, dass sich die zugrunde liegenden Datenbankdaten geändert haben, je nachdem, welches zuerst eintritt.
Das GridView-Objekt zeigt SqlCacheDependencies.aspx
Daten aus zwei Tabellen an – Products
und Categories
(das CategoryName
-Feld des Produkts wird über ein JOIN
auf Categories
abgerufen). Daher möchten wir zwei SQL-Cacheabhängigkeiten angeben: NorthwindDB:Products; NorthwindDB:Categories .
Abbildung 9: Konfigurieren der ObjectDataSource zur Unterstützung der Zwischenspeicherung mithilfe von SQL-Cacheabhängigkeiten Products
und Categories
(Klicken Sie hier, um das Bild in voller Größe anzuzeigen)
Nachdem Sie die ObjectDataSource so konfiguriert haben, dass die Zwischenspeicherung unterstützt wird, überprüfen Sie die Seite über einen Browser. Auch hier sollte der Text "Ausgelöstes Ereignis auswählen" auf der ersten Seite angezeigt werden, sollte aber beim Paging, Sortieren oder Klicken auf die Schaltflächen "Bearbeiten" oder "Abbrechen" weggehen. Dies liegt daran, dass die Daten nach dem Laden der Daten in den ObjectDataSource-Cache dort verbleiben, bis die Products
Categories
Tabellen geändert werden oder die Daten über gridView aktualisiert werden.
Nachdem Sie durch das Raster geblättert haben und das Fehlen des Textes zum 'Auswahlereignis' festgestellt haben, öffnen Sie ein neues Browserfenster und navigieren Sie zum Tutorial im Abschnitt "Grundlagen" unter "Bearbeiten", "Einfügen" und "Löschen" (~/EditInsertDelete/Basics.aspx
). Aktualisieren Sie den Namen oder den Preis eines Produkts. Zeigen Sie dann vom ersten Browserfenster eine andere Datenseite an, sortieren Sie das Raster, oder klicken Sie auf die Schaltfläche "Bearbeiten" einer Zeile. Dieses Mal sollte das Ereignis "Selecting" erneut angezeigt werden, da die zugrunde liegenden Daten des Datenbank geändert wurden (siehe Abbildung 10). Wenn der Text nicht angezeigt wird, warten Sie einige Augenblicke, und versuchen Sie es erneut. Denken Sie daran, dass der Abrufdienst alle Products
Millisekunden auf Änderungen an der pollTime
Tabelle überprüft, sodass es zu einer Verzögerung zwischen dem Aktualisieren der zugrunde liegenden Daten und dem Entfernen der zwischengespeicherten Daten kommt.
Abbildung 10: Ändern der Produkttabelle entfernt die zwischengespeicherten Produktdaten (Klicken, um das Bild in voller Größe anzuzeigen)
Schritt 6: Programmgesteuertes Arbeiten mit derSqlCacheDependency
Klasse
Im Lernprogramm "Zwischenspeichern von Daten in der Architektur" wurden die Vorteile der Verwendung einer separaten Zwischenspeicherungsebene in der Architektur im Gegensatz zur engen Kopplung der Zwischenspeicherung mit der ObjectDataSource untersucht. In diesem Lernprogramm haben wir eine ProductsCL
Klasse erstellt, um die programmgesteuerte Arbeit mit dem Datencache zu veranschaulichen. Verwenden Sie die SqlCacheDependency
Klasse, um SQL-Cacheabhängigkeiten in der Cacheebene zu verwenden.
Beim Umfragesystem muss ein SqlCacheDependency
Objekt einer bestimmten Kombination aus Datenbank und Tabelle zugeordnet werden. Mit dem folgenden Code wird beispielsweise ein SqlCacheDependency
Objekt erstellt, das auf der Tabelle der Northwind-Datenbank Products
basiert:
Dim productsTableDependency As _
New Caching.SqlCacheDependency("NorthwindDB", "Products")
Die beiden Eingabeparameter für den SqlCacheDependency
Konstruktor sind die Datenbank- bzw. Tabellennamen. Wie bei der Eigenschaft SqlCacheDependency
von ObjectDataSource entspricht der verwendete Datenbankname dem Wert, der im Attribut name
des Elements <add>
in Web.config
angegeben ist. Der Tabellenname ist der tatsächliche Name der Datenbanktabelle.
Verwenden Sie um ein SqlCacheDependency
-Element, das dem Datencache hinzugefügt wurde, zuzuordnen, eine der Insert
-Methodenüberladungen, die eine Abhängigkeit akzeptieren. Der folgende Code fügt dem Datencache einen Wert für eine unbestimmte Dauer hinzu, ordnet ihn jedoch einer SqlCacheDependency
Tabelle Products
zu. Kurz gesagt, der Wert verbleibt im Cache, bis er aufgrund von Speichereinschränkungen entfernt wird oder weil das Abrufsystem festgestellt hat, dass sich die Tabelle seit dem Products
Zwischenspeicher geändert hat.
Dim productsTableDependency As _
New Caching.SqlCacheDependency("NorthwindDB", "Products")
Cache.Insert(key, _
value, _
productsTableDependency, _
System.Web.Caching.Cache.NoAbsoluteExpiration, _
System.Web.Caching.Cache.NoSlidingExpiration)
Die Zwischenspeicherungsklasse ProductsCL
speichert derzeit Daten aus der Products
Tabelle mit einer zeitbasierten Ablaufdauer von 60 Sekunden. Lassen Sie uns diese Klasse aktualisieren, sodass stattdessen SQL-Cacheabhängigkeiten verwendet werden. Die ProductsCL
Klasse s-Methode AddCacheItem
, die für das Hinzufügen der Daten zum Cache verantwortlich ist, enthält derzeit den folgenden Code:
Private Sub AddCacheItem(ByVal rawKey As String, ByVal value As Object)
Dim DataCache As System.Web.Caching.Cache = HttpRuntime.Cache
' Make sure MasterCacheKeyArray(0) is in the cache - if not, add it
If DataCache(MasterCacheKeyArray(0)) Is Nothing Then
DataCache(MasterCacheKeyArray(0)) = DateTime.Now
End If
' Add a CacheDependency
Dim dependency As _
New Caching.CacheDependency(Nothing, MasterCacheKeyArray)
DataCache.Insert(GetCacheKey(rawKey), value, dependency, _
DateTime.Now.AddSeconds(CacheDuration), _
Caching.Cache.NoSlidingExpiration)
End Sub
Aktualisieren Sie diesen Code so, dass anstelle der SqlCacheDependency
Cacheabhängigkeit ein MasterCacheKeyArray
Objekt verwendet wird:
Private Sub AddCacheItem(ByVal rawKey As String, ByVal value As Object)
Dim DataCache As System.Web.Caching.Cache = HttpRuntime.Cache
' Add the SqlCacheDependency objects for Products
Dim productsTableDependency As New _
Caching.SqlCacheDependency("NorthwindDB", "Products")
DataCache.Insert(GetCacheKey(rawKey), value, productsTableDependency, _
Cache.NoAbsoluteExpiration, Caching.Cache.NoSlidingExpiration)
End Sub
Um diese Funktionalität zu testen, fügen Sie der Seite direkt unter der bereits vorhandenen ProductsDeclarative
GridView eine neue GridView hinzu. Legen Sie diese neue GridView-Eigenschaft ID
auf ProductsProgrammatic
fest und binden Sie sie über das Smarttag an eine neue ObjectDataSource mit dem Namen ProductsDataSourceProgrammatic
. Konfigurieren Sie die ObjectDataSource so, dass sie die ProductsCL
-Klasse verwendet, und legen Sie die Dropdownlisten in den Registerkarten SELECT und UPDATE auf GetProducts
bzw. UpdateProduct
fest.
Abbildung 11: Konfigurieren der ObjectDataSource für die Verwendung der Klasse (Zum Anzeigen des ProductsCL
Bilds mit voller Größe klicken)
Abbildung 12: Auswählen der GetProducts
Methode aus der SELECT-Registerkarte Drop-Down Liste (Klicken, um das Bild in voller Größe anzuzeigen)
Abbildung 13: Wählen Sie die UpdateProduct-Methode aus der Liste der Registerkarte "UPDATE" Drop-Down (Klicken Sie, um das Bild in voller Größe anzuzeigen)
Nach Abschluss des Assistenten zum Konfigurieren von Datenquellen erstellt Visual Studio BoundFields und CheckBoxFields im GridView für jedes Datenfeld. Wie bei der ersten zu dieser Seite hinzugefügten GridView entfernen Sie alle Felder außer ProductName
, CategoryName
und UnitPrice
und formatieren Sie diese Felder nach Ihren Vorstellungen. Aktivieren Sie im Smart-Tag "GridView" die Kontrollkästchen "Paging aktivieren", "Sortierung aktivieren" und "Bearbeitung aktivieren". Wie beim ProductsDataSourceDeclarative
ObjectDataSource legt Visual Studio die Eigenschaft ProductsDataSourceProgrammatic
des OldValuesParameterFormatString
ObjectDataSource auf original_{0}
fest. Damit das Bearbeitungsfeature von GridView ordnungsgemäß funktioniert, legen Sie diese Eigenschaft wieder auf {0}
(oder entfernen Sie die Eigenschaftszuweisung aus der deklarativen Syntax vollständig).
Nach Abschluss dieser Aufgaben sollte das resultierende deklarative GridView- und ObjectDataSource-Markup wie folgt aussehen:
<asp:GridView ID="ProductsProgrammatic" runat="server"
AutoGenerateColumns="False" DataKeyNames="ProductID"
DataSourceID="ProductsDataSourceProgrammatic" AllowPaging="True"
AllowSorting="True">
<Columns>
<asp:CommandField ShowEditButton="True" />
<asp:TemplateField HeaderText="Product" SortExpression="ProductName">
<EditItemTemplate>
<asp:TextBox ID="ProductName" runat="server"
Text='<%# Bind("ProductName") %>' />
<asp:RequiredFieldValidator ID="RequiredFieldValidator1"
ControlToValidate="ProductName" Display="Dynamic"
ErrorMessage="You must provide a name for the product."
SetFocusOnError="True"
runat="server">*</asp:RequiredFieldValidator>
</EditItemTemplate>
<ItemTemplate>
<asp:Label ID="Label2" runat="server"
Text='<%# Bind("ProductName") %>' />
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField DataField="CategoryName" HeaderText="Category"
ReadOnly="True" SortExpression="CategoryName" />
<asp:TemplateField HeaderText="Price" SortExpression="UnitPrice">
<EditItemTemplate>
$<asp:TextBox ID="UnitPrice" runat="server" Columns="8"
Text='<%# Bind("UnitPrice", "{0:N2}") %>'></asp:TextBox>
<asp:CompareValidator ID="CompareValidator1" runat="server"
ControlToValidate="UnitPrice" Display="Dynamic"
ErrorMessage="You must enter a valid currency value with no
currency symbols. Also, the value must be greater than
or equal to zero."
Operator="GreaterThanEqual" SetFocusOnError="True"
Type="Currency" ValueToCompare="0">*</asp:CompareValidator>
</EditItemTemplate>
<ItemStyle HorizontalAlign="Right" />
<ItemTemplate>
<asp:Label ID="Label1" runat="server"
Text='<%# Bind("UnitPrice", "{0:c}") %>' />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
<asp:ObjectDataSource ID="ProductsDataSourceProgrammatic" runat="server"
OldValuesParameterFormatString="{0}" SelectMethod="GetProducts"
TypeName="ProductsCL" UpdateMethod="UpdateProduct">
<UpdateParameters>
<asp:Parameter Name="productName" Type="String" />
<asp:Parameter Name="unitPrice" Type="Decimal" />
<asp:Parameter Name="productID" Type="Int32" />
</UpdateParameters>
</asp:ObjectDataSource>
Um die SQL-Cacheabhängigkeit in der Cacheebene zu testen, legen Sie einen Haltepunkt in der ProductCL
Klassenmethode AddCacheItem
fest, und starten Sie dann mit dem Debuggen. Wenn Sie SqlCacheDependencies.aspx
das erste Mal besuchen, sollte der Haltepunkt erreicht werden, da die Daten das erste Mal angefordert und im Cache gespeichert werden. Wechseln Sie als Nächstes zu einer anderen Seite in der GridView, oder sortieren Sie eine der Spalten. Dies bewirkt, dass gridView seine Daten erneut abfragt, aber die Daten sollten im Cache gefunden werden, da die Products
Datenbanktabelle nicht geändert wurde. Wenn die Daten wiederholt nicht im Cache gefunden werden, stellen Sie sicher, dass genügend Arbeitsspeicher auf Ihrem Computer verfügbar ist, und versuchen Sie es erneut.
Nachdem Sie ein paar Seiten der GridView durchlaufen haben, öffnen Sie ein zweites Browserfenster, und navigieren Sie zum Lernprogramm "Grundlagen" im Abschnitt "Bearbeiten", "Einfügen" und "Löschen" (~/EditInsertDelete/Basics.aspx
). Aktualisieren Sie einen Datensatz aus der Tabelle "Produkte", und zeigen Sie dann im ersten Browserfenster eine neue Seite an, oder klicken Sie auf eine der Sortierkopfzeilen.
In diesem Szenario sehen Sie eines von zwei Dingen: Entweder der Haltepunkt wird getroffen, was angibt, dass die zwischengespeicherten Daten aufgrund der Änderung in der Datenbank entfernt wurden; oder der Haltepunkt wird nicht getroffen, was bedeutet, dass SqlCacheDependencies.aspx
jetzt veraltete Daten angezeigt werden. Wenn der Haltepunkt nicht getroffen wird, liegt es wahrscheinlich daran, dass der Abfragedienst noch nicht ausgelöst wurde, nachdem die Daten geändert wurden. Denken Sie daran, dass der Abrufdienst alle Products
Millisekunden auf Änderungen an der pollTime
Tabelle überprüft, sodass es zu einer Verzögerung zwischen dem Aktualisieren der zugrunde liegenden Daten und dem Entfernen der zwischengespeicherten Daten kommt.
Hinweis
Diese Verzögerung wird wahrscheinlicher angezeigt, wenn Sie eines der Produkte über das GridView-Element SqlCacheDependencies.aspx
bearbeiten. Im Lernprogramm "Caching Data in der Architektur" haben wir die MasterCacheKeyArray
Cacheabhängigkeit hinzugefügt, um sicherzustellen, dass die Daten, die über die Methode der ProductsCL
Klasse UpdateProduct
bearbeitet werden, aus dem Cache entfernt werden. Diese Cacheabhängigkeit wurde jedoch beim Ändern der AddCacheItem
Methode weiter oben in diesem Schritt ersetzt. Daher zeigt die ProductsCL
Klasse weiterhin die zwischengespeicherten Daten an, bis das Abrufsystem die Änderung an der Products
Tabelle notiert. In Schritt 7 wird gezeigt, wie Die MasterCacheKeyArray
Cacheabhängigkeit erneut eingeführt wird.
Schritt 7: Zuordnen mehrerer Abhängigkeiten zu einem zwischengespeicherten Element
Erinnern Sie sich daran, dass die MasterCacheKeyArray
Cacheabhängigkeit verwendet wird, um sicherzustellen, dass alle produktbezogenen Daten aus dem Cache entfernt werden, wenn ein einzelnes Element, das darin verknüpft ist, aktualisiert wird. Die Methode speichert GetProductsByCategoryID(categoryID)
ProductsDataTables
Instanzen für jede eindeutige categoryID-Wert. Wenn eines dieser Objekte entfernt wird, stellt die MasterCacheKeyArray
Cacheabhängigkeit sicher, dass auch die anderen entfernt werden. Ohne diese Cacheabhängigkeit besteht die Möglichkeit, dass andere zwischengespeicherte Produktdaten veraltet sein können, wenn die zwischengespeicherten Daten geändert werden. Folglich ist es wichtig, dass die Abhängigkeit vom Cache bei Verwendung von SQL-Cacheabhängigkeiten beibehalten MasterCacheKeyArray
wird. Die Methode des Datencaches Insert
lässt jedoch nur ein einzelnes Abhängigkeitsobjekt zu.
Darüber hinaus müssen wir beim Arbeiten mit SQL-Cacheabhängigkeiten möglicherweise mehrere Datenbanktabellen als Abhängigkeiten zuordnen. Beispielsweise enthält die in der Klasse ProductsDataTable
zwischengespeicherte ProductsCL
die Namen der Kategorie und des Lieferanten für jedes Produkt, aber die Methode AddCacheItem
verwendet nur eine Abhängigkeit gegenüber Products
. Wenn der Benutzer den Namen einer Kategorie oder eines Lieferanten aktualisiert, verbleiben die zwischengespeicherten Produktdaten im Cache und sind veraltet. Daher möchten wir die zwischengespeicherten Produktdaten nicht nur von der Products
Tabelle, sondern auch von den Categories
Tabellen und Suppliers
Tabellen abhängig machen.
Die AggregateCacheDependency
Klasse stellt eine Möglichkeit zum Zuordnen mehrerer Abhängigkeiten zu einem Cacheelement bereit. Erstellen Sie zunächst eine AggregateCacheDependency
Instanz. Fügen Sie als Nächstes die Gruppe von Abhängigkeiten mithilfe der AggregateCacheDependency
s-Methode Add
hinzu. Übergeben Sie beim anschließenden Einfügen des Elements in den Datencache die AggregateCacheDependency
instanz.
Wenn sich eine der Abhängigkeiten der AggregateCacheDependency
Instanz ändert, wird das zwischengespeicherte Element entfernt.
Im Folgenden ist der aktualisierte Code für die Klasse ProductsCL
-Methode AddCacheItem
dargestellt. Die Methode erstellt die MasterCacheKeyArray
Cacheabhängigkeit zusammen mit SqlCacheDependency
Objekten für die Products
, Categories
und Suppliers
Tabellen. Diese werden alle in einem AggregateCacheDependency
Objekt namens aggregateDependencies
kombiniert, das dann an die Insert
Methode übergeben wird.
Private Sub AddCacheItem(ByVal rawKey As String, ByVal value As Object)
Dim DataCache As System.Web.Caching.Cache = HttpRuntime.Cache
' Make sure MasterCacheKeyArray(0) is in the cache - if not, add it.
If DataCache(MasterCacheKeyArray(0)) Is Nothing Then
DataCache(MasterCacheKeyArray(0)) = DateTime.Now
End If
'Create the CacheDependency
Dim masterCacheKeyDependency As _
New Caching.CacheDependency(Nothing, MasterCacheKeyArray)
' Add the SqlCacheDependency objects for Products, Categories, and Suppliers
Dim productsTableDependency As _
New Caching.SqlCacheDependency("NorthwindDB", "Products")
Dim categoriesTableDependency As _
New Caching.SqlCacheDependency("NorthwindDB", "Categories")
Dim suppliersTableDependency As _
New Caching.SqlCacheDependency("NorthwindDB", "Suppliers")
' Create an AggregateCacheDependency
Dim aggregateDependencies As New Caching.AggregateCacheDependency()
aggregateDependencies.Add(masterCacheKeyDependency, productsTableDependency, _
categoriesTableDependency, suppliersTableDependency)
DataCache.Insert(GetCacheKey(rawKey), value, aggregateDependencies, _
Caching.Cache.NoAbsoluteExpiration, Caching.Cache.NoSlidingExpiration)
End Sub
Testen Sie diesen neuen Code aus. Änderungen an den Products
, Categories
oder Suppliers
-Tabellen führen nun dazu, dass die zwischengespeicherten Daten entfernt werden. Darüber hinaus löscht die Methode der ProductsCL
Klasse UpdateProduct
, die beim Bearbeiten eines Produkts über GridView aufgerufen wird, die MasterCacheKeyArray
Cacheabhängigkeit, wodurch die zwischengespeicherten ProductsDataTable
entfernt werden und die Daten bei der nächsten Anforderung erneut abgerufen werden.
Hinweis
SQL-Cacheabhängigkeiten können auch mit Ausgabezwischenspeicherung verwendet werden. Eine Demonstration dieser Funktionalität finden Sie unter: Verwenden ASP.NET Ausgabezwischenspeicherung mit SQL Server.
Zusammenfassung
Beim Zwischenspeichern von Datenbankdaten verbleiben die Daten idealerweise im Cache, bis sie in der Datenbank geändert wird. Mit ASP.NET 2.0 können SQL-Cacheabhängigkeiten sowohl in deklarativen als auch in programmgesteuerten Szenarien erstellt und verwendet werden. Eine der Herausforderungen bei diesem Ansatz besteht darin, zu ermitteln, wann die Daten geändert wurden. Die Vollversionen von Microsoft SQL Server 2005 bieten Benachrichtigungsfunktionen, die eine Anwendung benachrichtigen können, wenn sich ein Abfrageergebnis geändert hat. Für die Express Edition von SQL Server 2005 und älteren Versionen von SQL Server muss stattdessen ein Polling-System verwendet werden. Glücklicherweise ist die Einrichtung der erforderlichen Abstimmungsinfrastruktur ziemlich einfach.
Glückliche Programmierung!
Weitere nützliche Informationen
Weitere Informationen zu den in diesem Lernprogramm erläuterten Themen finden Sie in den folgenden Ressourcen:
- Verwenden von Abfragebenachrichtigungen in Microsoft SQL Server 2005
- Erstellen einer Abfragebenachrichtigung
-
Zwischenspeichern in ASP.NET mit der
SqlCacheDependency
Klasse -
ASP.NET SQL Server-Registrierungstool (
aspnet_regsql.exe
) -
Übersicht über
SqlCacheDependency
Zum Autor
Scott Mitchell, Autor von sieben ASP/ASP.NET Büchern und Gründer von 4GuysFromRolla.com, arbeitet seit 1998 mit Microsoft Web Technologies zusammen. Scott arbeitet als unabhängiger Berater, Trainer und Schriftsteller. Sein neuestes Buch ist Sams Teach Yourself ASP.NET 2.0 in 24 Stunden. Er kann bei mitchell@4GuysFromRolla.comerreicht werden.
Besonderer Dank an
Diese Lernprogrammreihe wurde von vielen hilfreichen Prüfern überprüft. Leitende Prüfer für dieses Lernprogramm waren Marko Rangel, Teresa Murphy und Hilton Giesenow. Möchten Sie meine bevorstehenden MSDN-Artikel überprüfen? Wenn ja, schicken Sie mir eine Nachricht an mitchell@4GuysFromRolla.com.