Permanenter InstanzkontextDurable Instance Context

Dieses Beispiel veranschaulicht das Anpassen der Windows Communication Foundation (WCF)-Laufzeit, um permanente instanzkontexte zu aktivieren.This sample demonstrates how to customize the Windows Communication Foundation (WCF) runtime to enable durable instance contexts. Dabei wird als Sicherungsspeicher SQL Server 2005 (in diesem Fall SQL Server 2005 Express) verwendet.It uses SQL Server 2005 as its backing store (SQL Server 2005 Express in this case). Aber es bietet auch eine Möglichkeit, auf benutzerdefinierte Speichermechanismen zuzugreifen.However, it also provides a way to access custom storage mechanisms.

Hinweis

Die Setupprozedur und die Buildanweisungen für dieses Beispiel befinden sich am Ende dieses Themas.The setup procedure and build instructions for this sample are located at the end of this topic.

Dieses Beispiel umfasst die Erweiterung der Kanalschicht und der dienstmodellebene WCF.This sample involves extending both the channel layer and the service model layer of the WCF. Deshalb ist es notwendig, das zugrunde liegenden Konzept zu verstehen, bevor Sie sich mit den Implementierungsdetails beschäftigen.Therefore it is necessary to understand the underlying concepts before going into the implementation details.

Permanente Instanzkontexte sind sehr häufig in realen Szenarios anzutreffen.Durable instance contexts can be found in the real world scenarios quite often. Eine Warenkorb-Anwendung kann beispielsweise unterbrochen und am nächsten Tag fortgesetzt werden.A shopping cart application for example, has the ability to pause shopping halfway through and continue it on another day. Wenn der Warenkorb am nächsten Tag geöffnet wird, wird der ursprüngliche Kontext wiederhergestellt.So that when we visit the shopping cart the next day, our original context is restored. Es ist jedoch unbedingt zu beachten, dass die Warenkorb-Anwendung (auf dem Server) nicht die Warenkorb-Instanz beibehält, während die Verbindung zum Server getrennt ist.It is important to note that the shopping cart application (on the server) does not maintain the shopping cart instance while we are disconnected. Sie behält vielmehr ihren Zustand in einem permanenten Speichermedium bei und verwendet diesen Zustand, wenn eine neue Instanz für den wiederhergestellten Kontext erstellt wird.Instead, it persists its state into a durable storage media and uses it when constructing a new instance for the restored context. Aus diesem Grund handelt es sich bei der Dienstinstanz, die möglicherweise für denselben Kontext dient, nicht um dieselbe Instanz wie die vorherige Instanz (d. h. sie hat nicht dieselbe Speicheradresse).Therefore the service instance that may service for the same context is not the same as the previous instance (that is, it does not have the same memory address).

Permanenter Instanzkontext wird durch ein kleines Protokoll ermöglicht, das eine Kontext-ID zwischen dem Client und dem Dienst austauscht.Durable instance context is made possible by a small protocol that exchanges a context ID between the client and service. Diese Kontext-ID wird auf dem Client erstellt und zum Dienst übertragen.This context ID is created on the client and transmitted to the service. Wenn die Dienstinstanz erstellt wird, versucht die Dienstlaufzeit den beibehaltenen Zustand zu laden, der dieser Kontext-ID aus einer permanenten Speicherung entspricht (standardmäßig ist es eine SQL Server 2005-Datenbank).When the service instance is created, the service runtime tries to load the persisted state that corresponds to this context ID from a persistent storage (by default it is a SQL Server 2005 database). Wenn kein Zustand verfügbar ist, liegt die neue Instanz in ihrem Standardzustand vor.If no state is available the new instance has its default state. Die Dienstimplementierung verwendet ein benutzerdefiniertes Attribut, um Vorgänge zu kennzeichnen, die den Zustand der Dienstimplementierung ändern. Die Laufzeit kann dadurch die Dienstinstanz speichern, nachdem die Vorgänge aufgerufen wurden.The service implementation uses a custom attribute to mark operations that change the state of the service implementation so that the runtime can save the service instance after invoking them.

Anhand der vorangegangenen Beschreibung können leicht zwei Schritte bestimmt werden, um das Ziel zu erreichen:By the previous description, two steps can easily be distinguished to achieve the goal:

  1. Ändern Sie die Nachricht, mit der die Kontext-ID übertragen wird.Change the message that goes on the wire to carry the context ID.

  2. Ändern Sie das lokale Verhalten des Diensts, um die benutzerdefinierte Instanziierungslogik zu implementieren.Change the service local behavior to implement custom instancing logic.

Da der erste aufgeführte Schritt die gesendete Nachricht betrifft, sollte er als ein benutzerdefinierter Kanal implementiert und mit der Kanalschicht verknüpft werden.Because the first one in the list affects the messages on the wire it should be implemented as a custom channel and be hooked up to the channel layer. Der zweite Schritt beeinflusst nur das lokale Verhalten des Diensts und kann deshalb durch das Erweitern mehrerer Diensterweiterungspunkte implementiert werden.The latter only affects the service local behavior and therefore can be implemented by extending several service extensibility points. In den nächsten Abschnitten wird jede dieser Erweiterungen erläutert.In the next few sections, each of these extensions are discussed.

Permanenter InstanceContext-KanalDurable InstanceContext Channel

Als erstes wird eine Kanalschichterweiterung betrachtet.The first thing to look at is a channel layer extension. Der erste Schritt beim Schreiben eines benutzerdefinierten Kanals besteht darin, die Kommunikationsstruktur des Kanals festzulegen.The first step in writing a custom channel is to decide the communication structure of the channel. Da ein neues Versandprotokoll eingeführt wird, sollte der Kanal mit fast allen anderen Kanälen im Kanalstapel funktionieren.As a new wire protocol is being introduced the channel should work with almost any other channel in the channel stack. Deshalb sollte es alle Nachrichtenaustauschmuster unterstützen.Therefore it should support all the message exchange patterns. Die Kernfunktionalität des Kanals ändert sich jedoch nicht, unabhängig von seiner Kommunikationsstruktur.However, the core functionality of the channel is the same regardless of its communication structure. Genauer gesagt sollte der Kanal vom Client die Kontext-ID in die Nachrichten schreiben und vom Dienst sollte er diese Kontext-ID aus den Nachrichten lesen und an die höheren Ebenen weiterleiten.More specifically, from the client it should write the context ID to the messages and from the service it should read this context ID from the messages and pass it to the upper levels. Aus diesem Grund wird eine DurableInstanceContextChannelBase-Klasse erstellt, die als die abstrakte Basisklasse für alle Implementierungen von permanenten Instanzkontext-Kanälen handelt.Because of that, a DurableInstanceContextChannelBase class is created that acts as the abstract base class for all durable instance context channel implementations. Diese Klasse enthält die allgemeinen Computerverwaltungsfunktionen und zwei geschützte Member, um die Kontextinformationen auf Nachrichten anzuwenden und von ihnen zu lesen.This class contains the common state machine management functions and two protected members to apply and read the context information to and from messages.

class DurableInstanceContextChannelBase  
{  
  //…  
  protected void ApplyContext(Message message)  
  {  
    //…              
  }  
  protected string ReadContextId(Message message)  
  {  
    //…              
  }  
}  

Diese beiden Methoden nutzen IContextManager-Implementierungen, um die Kontext-ID auf Nachrichten zu schreiben und von ihnen zu lesen.These two methods make use of IContextManager implementations to write and read the context ID to or from the message. (IContextManager ist eine benutzerdefinierte Schnittstelle, die verwendet wird, um den Vertrag für alle Kontext-Manager zu definieren.) Der Kanal kann entweder die Kontext-ID in einem benutzerdefinierten SOAP-Header oder in einem HTTP-Cookieheader enthalten.(IContextManager is a custom interface used to define the contract for all context managers.) The channel can either include the context ID in a custom SOAP header or in a HTTP cookie header. Jede Kontextmanagerimplementierung erbt von der ContextManagerBase-Klasse, die die allgemeine Funktionalität für alle Kontextmanager enthält.Each context manager implementation inherits from the ContextManagerBase class that contains the common functionality for all context managers. Die GetContextId-Methode in dieser Klasse wird verwendet, um die Kontext-ID vom Client zu erzeugen.The GetContextId method in this class is used to originate the context ID from the client. Wenn eine Kontext-ID ist zum ersten Mal erzeugt wird, wird sie mithilfe dieser Methode in einer Textdatei gespeichert, deren Name von der Remote-Endpunktadresse erstellt wird (in den typischen URI werden ungültige Zeichen im Dateinamen durch @-Zeichen ersetzt).When a context ID is originated for the first time, this method saves it into a text file whose name is constructed by the remote endpoint address (the invalid file name characters in the typical URIs are replaced with @ characters).

Wird die Kontext-ID später für denselben Remote-Endpunkt benötigt, überprüft diese Methode, ob eine entsprechende Datei vorhanden ist.Later when the context ID is required for the same remote endpoint, it checks whether an appropriate file exists. Wenn dies der Fall ist, liest sie die Kontext-ID und gibt sie zurück.If it does, it reads the context ID and returns. Andernfalls gibt sie eine neu generierte Kontext-ID zurück und speichert sie in einer Datei.Otherwise it returns a newly generated context ID and saves it to a file. In der Standardkonfiguration werden diese Dateien in einem Verzeichnis namens "ContextStore" abgelegt. Es befindet sich im temporären Verzeichnis des aktuellen Benutzers.With the default configuration, these files are placed in a directory called ContextStore, which resides in the current user’s temp directory. Dieser Speicherort ist jedoch mit dem Bindungselement konfigurierbar.However this location is configurable using the binding element.

Der für den Transport der Kontext-ID verwendete Mechanismus kann konfiguriert werden.The mechanism used to transport the context ID is configurable. Er kann entweder in den HTTP-Cookieheader oder einen benutzerdefinierten SOAP-Header geschrieben werden.It could be either written to the HTTP cookie header or to a custom SOAP header. Wenn Sie sich für den benutzerdefinierten SOAP-Header entscheiden, kann dieses Protokoll mit anderen als HTTP-Protokollen (z. B. TCP oder Benannte Pipes) verwendet werden.The custom SOAP header approach makes it possible to use this protocol with non-HTTP protocols (for example, TCP or Named Pipes). Es gibt zwei Klassen, nämlich MessageHeaderContextManager und HttpCookieContextManager, die diese beiden Optionen implementieren.There are two classes, namely MessageHeaderContextManager and HttpCookieContextManager, which implement these two options.

Beide Klassen schreiben die Kontext-ID richtig in die Nachricht.Both of them write the context ID to the message appropriately. Die MessageHeaderContextManager-Klasse schreibt sie beispielsweise in einen SOAP-Header in der WriteContext-Methode.For example, the MessageHeaderContextManager class writes it to a SOAP header in the WriteContext method.

public override void WriteContext(Message message)  
{  
  string contextId = this.GetContextId();  

  MessageHeader contextHeader =  
    MessageHeader.CreateHeader(DurableInstanceContextUtility.HeaderName,  
      DurableInstanceContextUtility.HeaderNamespace,  
      contextId,   
      true);  

  message.Headers.Add(contextHeader);  
}   

Sowohl die ApplyContext-Methode als auch die ReadContextId-Methode in der DurableInstanceContextChannelBase-Klasse rufen IContextManager.ReadContext bzw. IContextManager.WriteContext auf.Both the ApplyContext and ReadContextId methods in the DurableInstanceContextChannelBase class invoke the IContextManager.ReadContext and IContextManager.WriteContext, respectively. Diese Kontext-Manager werden jedoch nicht direkt von der DurableInstanceContextChannelBase-Klasse erstellt.However, these context managers are not directly created by the DurableInstanceContextChannelBase class. Diese Aufgabe wird von der ContextManagerFactory-Klasse ausgeführt.Instead it uses the ContextManagerFactory class to do that job.

IContextManager contextManager =  
                ContextManagerFactory.CreateContextManager(contextType,   
                this.contextStoreLocation,   
                this.endpointAddress);  

Die ApplyContext-Methode wird von den Sendekanälen aufgerufen.The ApplyContext method is invoked by the sending channels. Sie fügt die Kontext-ID in die ausgehenden Nachrichten ein.It injects the context ID to the outgoing messages. Die ReadContextId-Methode wird von den Empfangskanälen aufgerufen.The ReadContextId method is invoked by the receiving channels. Diese Methode stellt sicher, dass die Kontext-ID in den eingehenden Nachrichten verfügbar ist und fügt sie zur Properties-Auflistung der Message-Klasse hinzu.This method ensures that the context ID is available in the incoming messages and adds it to the Properties collection of the Message class. Wenn ein Fehler beim Lesen der Kontext-ID auftritt und der Kanal abgebrochen wird, löst diese Methode eine CommunicationException aus.It also throws a CommunicationException in case of a failure to read the context ID and thus causes the channel to be aborted.

message.Properties.Add(DurableInstanceContextUtility.ContextIdProperty, contextId);  

Bevor Sie fortfahren, ist es wichtig, die Verwendung der Properties-Auflistung in der Message-Klasse zu verstehen.Before proceeding, it is important to understand the usage of the Properties collection in the Message class. In der Regel wird diese Properties-Auflistung verwendet, wenn Daten von unteren an die obere Ebenen der Kanalschicht weitergegeben werden.Typically, this Properties collection is used when passing data from lower to the upper levels from the channel layer. So können die gewünschten Daten den oberen Ebenen konsistent und unabhängig von den Protokolldetails zur Verfügung gestellt werden.This way the desired data can be provided to the upper levels in a consistent manner regardless of the protocol details. Anders ausgedrückt kann die Kanalschicht die Kontext-ID als SOAP-Header oder HTTP-Cookieheader senden und empfangen.In other words, the channel layer can send and receive the context ID either as a SOAP header or a HTTP cookie header. Die oberen Ebenen müssen diese Details jedoch nicht kennen, da die Kanalschicht diese Informationen in der Properties-Auflistung verfügbar macht.But it is not necessary for the upper levels to know about these details because the channel layer makes this information available in the Properties collection.

Wenn die DurableInstanceContextChannelBase-Klasse vorhanden ist, müssen alle zehn erforderlichen Schnittstellen (IOutputChannel, IInputChannel, IOutputSessionChannel, IInputSessionChannel, IRequestChannel, IReplyChannel, IRequestSessionChannel, IReplySessionChannel, IDuplexChannel, IDuplexSessionChannel) implementiert werden.Now with the DurableInstanceContextChannelBase class in place all ten of the necessary interfaces (IOutputChannel, IInputChannel, IOutputSessionChannel, IInputSessionChannel, IRequestChannel, IReplyChannel, IRequestSessionChannel, IReplySessionChannel, IDuplexChannel, IDuplexSessionChannel) must be implemented. Sie ähneln jedem verfügbaren Nachrichtenaustauschmuster (Datagramm, Simplex, Duplex und deren sitzungsbasierten Varianten).They resemble every available message exchange pattern (datagram, simplex, duplex and their sessionful variants). Jede dieser Implementierungen erbt die vorher beschriebene Basisklasse und ruft ApplyContext und ReadContexId entsprechend auf.Each of these implementations inherit the base class previously described and calls ApplyContext and ReadContexId appropriately. So ruft beispielsweise DurableInstanceContextOutputChannel – die die IOutputChannel-Schnittstelle implementiert – die ApplyContext-Methode von jeder Methode auf, die Nachrichten sendet.For example, DurableInstanceContextOutputChannel - which implements the IOutputChannel interface - calls the ApplyContext method from each method that sends the messages.

public void Send(Message message, TimeSpan timeout)  
{  
    // Apply the context information before sending the message.  
    this.ApplyContext(message);  
    //…  
}   

Die DurableInstanceContextInputChannel wiederum – die die IInputChannel-Schnittstelle implementiert – ruft die ReadContextId-Methode in jeder Methode auf, die Nachrichten empfängt.On the other hand, DurableInstanceContextInputChannel - which implements the IInputChannel interface - calls the ReadContextId method in each method which receives the messages.

public Message Receive(TimeSpan timeout)  
{  
    //…  
      ReadContextId(message);  
      return message;  
}  

Außerdem delegieren diese Kanalimplementierungen die Methodenaufrufe an den nächst unteren Kanal im Kanalstapel.Apart from this, these channel implementations delegate the method invocations to the channel below them in the channel stack. Sitzungsbasierte Varianten verfügen jedoch über eine Grundlogik, um sicherzustellen, dass die Kontext-ID gesendet und nur für die erste Nachricht gelesen wird, aufgrund derer die Sitzung erstellt wurde.However, sessionful variants have a basic logic to make sure that the context ID is sent and is read only for the first message that causes the session to be created.

if (isFirstMessage)  
{  
//…  
    this.ApplyContext(message);  
    isFirstMessage = false;  
}  

Diese kanalimplementierungen werden dann hinzugefügt, für die WCF-Kanal-Laufzeit von der DurableInstanceContextBindingElement Klasse und DurableInstanceContextBindingElementSection -kanallaufzeit.These channel implementations are then added to the WCF channel runtime by the DurableInstanceContextBindingElement class and DurableInstanceContextBindingElementSection class appropriately. Finden Sie unter der HttpCookieSession channel Beispieldokumentation für ausführliche Informationen zu Bindungselementen und Elementabschnitte binden.See the HttpCookieSession channel sample documentation for more details about binding elements and binding element sections.

Erweiterungen der DienstmodellebeneService Model Layer Extensions

Nachdem nun die Kontext-ID die Kanalschicht durchlaufen hat, kann das Dienstverhalten implementiert werden, um die Instanziierung benutzerspezifisch anzupassen.Now that the context ID has traveled through the channel layer, the service behavior can be implemented to customize the instantiation. In diesem Beispiel wird ein Speicher-Manager verwendet, um den Zustand aus dem permanenten Speicher zu laden und in ihm zu speichern.In this sample, a storage manager is used to load and save state from or to the persistent store. Wie bereits erläutert arbeitet dieses Beispiel mit einem Speicher-Manager, der SQL Server 2005 als Sicherungsspeicher verwendet.As explained previously, this sample provides a storage manager that uses SQL Server 2005 as its backing store. Es ist aber auch möglich, benutzerdefinierte Speichermechanismen zu dieser Erweiterung hinzuzufügen.However, it is also possible to add custom storage mechanisms to this extension. Dazu wird eine öffentliche Schnittstelle deklariert, die von allen Speicher-Managern implementiert werden muss.To do that a public interface is declared, which must be implemented by all storage managers.

public interface IStorageManager  
{  
    object GetInstance(string contextId, Type type);  
    void SaveInstance(string contextId, object state);  
}  

Die SqlServerStorageManager-Klasse enthält die IStorageManager-Standardimplementierung.The SqlServerStorageManager class contains the default IStorageManager implementation. In ihrer SaveInstance-Methode wird das entsprechende Objekt mit dem XmlSerializer serialisiert und in einer SQL Server-Datenbank gespeichert.In its SaveInstance method the given object is serialized using the XmlSerializer and is saved to the SQL Server database.

XmlSerializer serializer = new XmlSerializer(state.GetType());  
string data;  

using (StringWriter writer = new StringWriter(CultureInfo.InvariantCulture))  
{  
    serializer.Serialize(writer, state);  
    data = writer.ToString();  
}  

using (SqlConnection connection = new SqlConnection(GetConnectionString()))  
{  
    connection.Open();  

    string update = @"UPDATE Instances SET Instance = @instance WHERE ContextId = @contextId";  

    using (SqlCommand command = new SqlCommand(update, connection))  
    {  
        command.Parameters.Add("@instance", SqlDbType.VarChar, 2147483647).Value = data;  
        command.Parameters.Add("@contextId", SqlDbType.VarChar, 256).Value = contextId;  

        int rows = command.ExecuteNonQuery();  

        if (rows == 0)  
        {  
            string insert = @"INSERT INTO Instances(ContextId, Instance) VALUES(@contextId, @instance)";  
            command.CommandText = insert;  
            command.ExecuteNonQuery();  
        }  
    }  
}  

In der GetInstance-Methode werden die serialisierten Daten für eine angegebene Kontext-ID gelesen, und das daraus erstellte Objekt wird an den Aufrufer zurückgegeben.In the GetInstance method the serialized data is read for a given context ID and the object constructed from it is returned to the caller.

object data;  
using (SqlConnection connection = new SqlConnection(GetConnectionString()))  
{  
    connection.Open();  

    string select = "SELECT Instance FROM Instances WHERE ContextId = @contextId";  
    using (SqlCommand command = new SqlCommand(select, connection))  
    {  
        command.Parameters.Add("@contextId", SqlDbType.VarChar, 256).Value = contextId;  
        data = command.ExecuteScalar();  
    }  
}  

if (data != null)  
{  
    XmlSerializer serializer = new XmlSerializer(type);  
    using (StringReader reader = new StringReader((string)data))  
    {  
        object instance = serializer.Deserialize(reader);  
        return instance;  
    }  
}  

Benutzer dieser Speicher-Manager sollten sie nicht direkt instanziieren.Users of these storage managers are not supposed to instantiate them directly. Sie verwenden die StorageManagerFactory-Klasse, die von den Speichermanagererstellungsdetails abstrahiert wird.They use the StorageManagerFactory class, which abstracts from the storage manager creation details. Diese Klasse verfügt über einen statischen Member, GetStorageManager, der eine Instanz eines gegebenen Speichermanagertyps erstellt.This class has one static member, GetStorageManager, which creates an instance of a given storage manager type. Wenn der Typparameter null lautet, erstellt diese Methode eine Instanz der SqlServerStorageManager-Standardklasse und gibt diese zurück.If the type parameter is null, this method creates an instance of the default SqlServerStorageManager class and returns it. Sie überprüft auch den gegebenen Typ, um sicherzustellen, dass er die IStorageManager-Schnittstelle implementiert.It also validates the given type to make sure that it implements the IStorageManager interface.

public static IStorageManager GetStorageManager(Type storageManagerType)  
{  
IStorageManager storageManager = null;  

if (storageManagerType == null)  
{  
    return new SqlServerStorageManager();  
}  
else  
{  
    object obj = Activator.CreateInstance(storageManagerType);  

    // Throw if the specified storage manager type does not  
    // implement IStorageManager.  
    if (obj is IStorageManager)  
    {  
        storageManager = (IStorageManager)obj;  
    }  
    else  
    {  
        throw new InvalidOperationException(  
                  ResourceHelper.GetString("ExInvalidStorageManager"));  
    }  

    return storageManager;  
}                  
}   

Die notwendige Infrastruktur zum Lesen und Schreiben von Instanzen aus dem permanenten Speicher wurde implementiert.The necessary infrastructure to read and write instances from the persistent storage is implemented. Jetzt müssen die notwendigen Schritte zum Ändern des Dienstverhaltens durchgeführt werden.Now the necessary steps to change the service behavior have to be taken.

Zuerst muss die Kontext-ID gespeichert werden, die über die Kanalschicht zur aktuellen InstanceContext übertragen wurde.As the first step of this process we have to save the context ID, which came through the channel layer to the current InstanceContext. InstanceContext ist eine Laufzeitkomponente, die als Bindeglied zwischen der WCF-Verteiler und der Dienstinstanz fungiert.InstanceContext is a runtime component that acts as the link between the WCF dispatcher and the service instance. Sie kann verwendet werden, um der Dienstinstanz einen zusätzlichen Zustand und zusätzliches Verhalten bereitzustellen.It can be used to provide additional state and behavior to the service instance. Dies ist notwendig, da die Kontext-ID in einer sitzungsbasierten Kommunikation nur mit der ersten Nachricht gesendet wird.This is essential because in sessionful communication the context ID is sent only with the first message.

WCF ermöglicht, seine InstanceContext-Laufzeitkomponente zu erweitern, indem ein Zustand "Neu" und das Verhalten mithilfe des erweiterbaren Objektmusters hinzugefügt.WCF allows extending its InstanceContext runtime component by adding a new state and behavior using its extensible object pattern. Das erweiterbare Objektmuster wird in WCF verwendet, um vorhandene Laufzeitklassen neue Funktionen zu erweitern oder um neue Zustandsfunktionen zu einem Objekt hinzuzufügen.The extensible object pattern is used in WCF to either extend existing runtime classes with new functionality or to add new state features to an object. Es gibt drei Schnittstellen im erweiterbaren Objektmuster – IExtensibleObject<T >, IExtension<T >, und IExtensionCollection<T >:There are three interfaces in the extensible object pattern - IExtensibleObject<T>, IExtension<T>, and IExtensionCollection<T>:

  • Die IExtensibleObject<T >-Schnittstelle wird von Objekten implementiert, die Erweiterungen zulassen, die ihre Funktionalität anpassen.The IExtensibleObject<T> interface is implemented by objects that allow extensions that customize their functionality.

  • Die IExtension<T >-Schnittstelle wird von Objekten implementiert, die Erweiterungen der Klassen des Typs t sind.The IExtension<T> interface is implemented by objects that are extensions of classes of type T.

  • Die IExtensionCollection<T >-Schnittstelle ist eine Auflistung von iextensions, die ein Abrufen von ihrem Typ iextensions ermöglichen.The IExtensionCollection<T> interface is a collection of IExtensions that allows for retrieving IExtensions by their type.

Es sollte deshalb eine InstanceContextExtension-Klasse erstellt werden, die die IExtension-Schnittstelle implementiert und den erforderlichen Zustand zum Speichern der Kontext-ID definiert.Therefore an InstanceContextExtension class should be created that implements the IExtension interface and defines the required state to save the context ID. Diese Klasse bietet auch den Zustand, um den verwendeten Speicher-Manager aufzunehmen.This class also provides the state to hold the storage manager being used. Sobald der neue Zustand gespeichert ist, sollte es nicht mehr möglich sein, ihn zu ändern.Once the new state is saved, it should not be possible to modify it. Deshalb wird der Zustand zum Erstellungszeitpunkt bereitgestellt und in der Instanz gespeichert. Anschließend kann nur über schreibgeschützte Eigenschaften darauf zugegriffen werden.Therefore the state is provided and saved to the instance at the time it is being constructed and then only accessible using read-only properties.

// Constructor  
public DurableInstanceContextExtension(string contextId,   
            IStorageManager storageManager)  
{  
    this.contextId = contextId;  
    this.storageManager = storageManager;              
}  

// Read only properties  
public string ContextId  
{  
    get { return this.contextId; }  
}  

public IStorageManager StorageManager  
{  
    get { return this.storageManager; }              
}   

Die InstanceContextInitializer-Klasse implementiert die IInstanceContextInitializer-Schnittstelle und fügt die Instanzkontexterweiterung zur Erweiterungsauflistung der erstellten Komponente InstanceContext hinzu.The InstanceContextInitializer class implements the IInstanceContextInitializer interface and adds the instance context extension to the Extensions collection of the InstanceContext being constructed.

public void Initialize(InstanceContext instanceContext, Message message)  
{  
    string contextId =   
  (string)message.Properties[DurableInstanceContextUtility.ContextIdProperty];  

    DurableInstanceContextExtension extension =  
                new DurableInstanceContextExtension(contextId,   
                     storageManager);  
    instanceContext.Extensions.Add(extension);  
}  

Wie bereits beschrieben wird die Kontext-ID von der Properties-Auflistung der Message-Klasse gelesen und an den Konstruktor der Erweiterungsklasse weitergegeben.As described earlier the context ID is read from the Properties collection of the Message class and passed to the constructor of the extension class. Dadurch wird veranschaulicht, wie Informationen zwischen den Schichten konsistent ausgetauscht werden können.This demonstrates how information can be exchanged between the layers in a consistent manner.

Im nächsten wichtigen Schritt wird der Vorgang zum Erstellen der Dienstinstanz überschrieben.The next important step is overriding the service instance creation process. WCF ermöglicht das Implementieren von benutzerdefinierten Verhaltensweisen und Einbinden dieser bis zu der Laufzeit unter Verwendung der IInstanceProvider-Schnittstelle.WCF allows implementing custom instantiation behaviors and hooking them up to the runtime using the IInstanceProvider interface. Die neue InstanceProvider-Klasse wird implementiert, um diese Aufgabe auszuführen.The new InstanceProvider class is implemented to do that job. Im Konstruktor wird der vom Instanzenanbieter erwartete Diensttyp akzeptiert.In the constructor the service type expected from the instance provider is accepted. Später wird dies verwendet, um neue Instanzen zu erstellen.Later this is used to create new instances. In der GetInstance-Implementierung wird eine Instanz eines Speicher-Managers erstellt, die nach einer beibehaltenen Instanz sucht.In the GetInstance implementation an instance of a storage manager is created looking for a persisted instance. Wenn sie null zurückgibt, wird eine neue Instanz des Diensttyps instanziiert und zum Aufrufer zurückgegeben.If it returns null then a new instance of the service type is instantiated and returned to the caller.

public object GetInstance(InstanceContext instanceContext, Message message)  
{  
    object instance = null;  

    DurableInstanceContextExtension extension =  
    instanceContext.Extensions.Find<DurableInstanceContextExtension>();  

    string contextId = extension.ContextId;  
    IStorageManager storageManager = extension.StorageManager;              

    instance = storageManager.GetInstance(contextId, serviceType);          

    if (instance == null)  
    {  
        instance = Activator.CreateInstance(serviceType);  
    }  

    return instance;  
}  

Im nächsten wichtigen Schritt wird die InstanceContextExtension, InstanceContextInitializer-Klasse und die InstanceProvider-Klasse in die Dienstmodellaufzeit installiert.The next important step is to install the InstanceContextExtension, InstanceContextInitializer and InstanceProvider classes into the service model runtime. Es kann ein benutzerdefiniertes Attribut verwendet werden, um die Dienstimplementierungsklassen für die Installation des Verhaltens zu kennzeichnen.A custom attribute could be used to mark the service implementation classes to install the behavior. DurableInstanceContextAttribute enthält die Implementierung für dieses Attribut und implementiert die IServiceBehavior-Schnittstelle, um die gesamte Dienstlaufzeit zu erweitern.The DurableInstanceContextAttribute contains the implementation for this attribute and it implements the IServiceBehavior interface to extend the entire service runtime.

Diese Klasse verfügt über eine Eigenschaft, die den Typ des zu verwendenden Speicher-Managers akzeptiert.This class has a property that accepts the type of the storage manager to be used. So ermöglicht es die Implementierung den Benutzern, ihre eigene IStorageManager-Implementierung als Parameter dieses Attributs anzugeben.In this way the implementation enables the users to specify their own IStorageManager implementation as parameter of this attribute.

In der ApplyDispatchBehavior-Implementierung wird der InstanceContextMode des aktuellen ServiceBehavior-Attributs überprüft.In the ApplyDispatchBehavior implementation the InstanceContextMode of the current ServiceBehavior attribute is being verified. Wenn diese Eigenschaft auf "Singleton" festgelegt ist, kann keine permanente Instanziierung aktualisiert werden und InvalidOperationException wird ausgelöst, um den Host zu benachrichtigen.If this property is set to Singleton, enabling durable instancing is not possible and an InvalidOperationException is thrown to notify the host.

ServiceBehaviorAttribute serviceBehavior =  
    serviceDescription.Behaviors.Find<ServiceBehaviorAttribute>();  

if (serviceBehavior != null &&  
     serviceBehavior.InstanceContextMode == InstanceContextMode.Single)  
{  
    throw new InvalidOperationException(  
       ResourceHelper.GetString("ExSingeltonInstancingNotSupported"));  
}  

Anschließend werden die Instanzen des Speicher-Managers, des Instanzkontextinitialisierers und des Instanzenanbieters erstellt und in der für jeden Endpunkt erstellten DispatchRuntime installiert.After this the instances of the storage manager, instance context initializer, and the instance provider are created and installed in the DispatchRuntime created for every endpoint.

IStorageManager storageManager =   
    StorageManagerFactory.GetStorageManager(storageManagerType);  

InstanceContextInitializer contextInitializer =  
    new InstanceContextInitializer(storageManager);  

InstanceProvider instanceProvider =  
    new InstanceProvider(description.ServiceType);  

foreach (ChannelDispatcherBase cdb in serviceHostBase.ChannelDispatchers)  
{  
    ChannelDispatcher cd = cdb as ChannelDispatcher;  

    if (cd != null)  
    {  
        foreach (EndpointDispatcher ed in cd.Endpoints)  
        {  
            ed.DispatchRuntime.InstanceContextInitializers.Add(contextInitializer);  
            ed.DispatchRuntime.InstanceProvider = instanceProvider;  
        }  
    }  
}  

Bisher resultiert aus diesem Beispiel ein Kanal, der das benutzerdefinierte Versandprotokoll für den Austausch der benutzerdefinierten Kontext-ID aktiviert hat. Außerdem überschreibt es das Standardinstanziierungsverhalten, die Instanzen aus dem permanenten Speicher zu laden.In summary so far, this sample has produced a channel that enabled the custom wire protocol for custom context ID exchange and it also overwrites the default instancing behavior to load the instances from the persistent storage.

Nun muss nur noch die Dienstinstanz im permanenten Speicher gespeichert werden.What is left is a way to save the service instance to the persistent storage. Wie zuvor erläutert gibt es bereits die erforderliche Funktionalität, den Zustand in einer IStorageManager-Implementierung zu speichern.As discussed previously, there is already the required functionality to save the state in an IStorageManager implementation. Wir müssen dies nun in der WCF-Laufzeit integrieren.We now must integrate this with the WCF runtime. Es ist ein weiteres Attribut erforderlich, dass auf die Methoden in der Dienstimplementierungsklasse angewendet werden kann.Another attribute is required that is applicable to the methods in the service implementation class. Dieses Attribut soll auf die Methoden angewendet werden, die den Zustand der Dienstinstanz ändern.This attribute is supposed to be applied to the methods that change the state of the service instance.

Die SaveStateAttribute-Klasse implementiert diese Funktionalität.The SaveStateAttribute class implements this functionality. Es implementiert auch IOperationBehavior Klasse, um die WCF-Laufzeit für jeden Vorgang zu ändern.It also implements IOperationBehavior class to modify the WCF runtime for each operation. Wenn eine Methode mit diesem Attribut gekennzeichnet ist, die WCF-Laufzeit ruft der ApplyBehavior Methode während der entsprechende DispatchOperation erstellt wird.When a method is marked with this attribute, the WCF runtime invokes the ApplyBehavior method while the appropriate DispatchOperation is being constructed. In dieser Methodenimplementierung ist eine Codezeile vorhanden:In this method implementation there is single line of code:

dispatch.Invoker = new OperationInvoker(dispatch.Invoker);  

Die Anweisung erstellt eine Instanz des OperationInvoker-Typs und weist sie zur Invoker-Eigenschaft des erstellten DispatchOperation zu.This instruction creates an instance of OperationInvoker type and assigns it to the Invoker property of the DispatchOperation being constructed. Die OperationInvoker-Klasse ist ein Wrapper des Standardvorgangaufrufers, der für DispatchOperation erstellt wurde.The OperationInvoker class is a wrapper for the default operation invoker created for the DispatchOperation. Diese Klasse implementiert die IOperationInvoker-Schnittstelle.This class implements the IOperationInvoker interface. In der Invoke-Methodenimplementierung wird der tatsächliche Methodenaufruf an den internen Vorgangsaufrufer delegiert.In the Invoke method implementation the actual method invocation is delegated to the inner operation invoker. Bevor die Ergebnisse jedoch zurückgegeben werden, wird mit dem Speicher-Manager in InstanceContext die Dienstinstanz gespeichert.However, before returning the results the storage manager in the InstanceContext is used to save the service instance.

object result = innerOperationInvoker.Invoke(instance,  
    inputs, out outputs);  

// Save the instance using the storage manager saved in the   
// current InstanceContext.  
InstanceContextExtension extension =  
    OperationContext.Current.InstanceContext.Extensions.Find<InstanceContextExtension>();  

extension.StorageManager.SaveInstance(extension.ContextId, instance);  
return result;  

Verwenden der ErweiterungUsing the Extension

Sowohl die Kanalschicht und Erweiterungen der dienstmodellebene fertig sind, und sie können jetzt in WCF-Anwendungen verwendet werden.Both the channel layer and service model layer extensions are done and they can now be used in WCF applications. Dienste müssen den Kanal mithilfe einer benutzerdefinierten Bindung zum Kanalstapel hinzufügen und die Dienstimplementierungsklassen dann mit den entsprechenden Attributen kennzeichnen.Services have to add the channel into the channel stack using a custom binding and then mark the service implementation classes with the appropriate attributes.

[DurableInstanceContext]  
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)]  
public class ShoppingCart : IShoppingCart  
{  
//…  
     [SaveState]  
     public int AddItem(string item)  
     {  
         //…  
     }  
//…  
 }  

Clientanwendungen müssen den DurableInstanceContextChannel mit einer benutzerdefinierten Bindung zum Kanalstapel hinzufügen.Client applications must add the DurableInstanceContextChannel into the channel stack using a custom binding. Um den Kanal deklarativ in der Konfigurationsdatei zu konfigurieren, muss der Bindungselementbereich zur Auflistung der Bindungselementerweiterungen hinzugefügt werden.To configure the channel declaratively in the configuration file, the binding element section has to be added to the binding element extensions collection.

<system.serviceModel>  
 <extensions>  
   <bindingElementExtensions>  
     <add name="durableInstanceContext"  
type="Microsoft.ServiceModel.Samples.DurableInstanceContextBindingElementSection, DurableInstanceContextExtension, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>  
   </bindingElementExtensions>  
 </extensions>  

Jetzt kann das Bindungselement wie andere Standardbindungselemente mit einer benutzerdefinierten Bindung verwendet werden:Now the binding element can be used with a custom binding just like other standard binding elements:

<bindings>  
 <customBinding>  
   <binding name="TextOverHttp">  
     <durableInstanceContext contextType="HttpCookie"/>             
     <reliableSession />  
     <textMessageEncoding />  
     <httpTransport />  
   </binding>  
 </customBinding>  
</bindings>  

SchlussbemerkungConclusion

In diesem Beispiel wurde gezeigt, wie ein benutzerdefinierter Protokollkanal erstellt wird und wie das Dienstverhalten angepasst werden muss, um diesen Protokollkanal zu aktivieren.This sample showed how to create a custom protocol channel and how to customize the service behavior to enable it.

Die Erweiterung kann weiter verbessert werden, wenn Benutzer die IStorageManager-Implementierung mit einem Konfigurationsabschnitt angeben können.The extension can be further improved by letting users specify the IStorageManager implementation using a configuration section. Dadurch kann der Sicherungsspeicher geändert werden, ohne den Dienstcode neu zu kompilieren.This makes it possible to modify the backing store without recompiling the service code.

Außerdem können Sie versuchen, eine Klasse zu implementieren (z. B. StateBag), die den Zustand der Instanz kapselt.Furthermore you could try to implement a class (for example, StateBag), which encapsulates the state of the instance. Diese Klasse ist für das Beibehalten des Zustands verantwortlich, wenn er sich ändert.That class is responsible for persisting the state whenever it changes. Sie können so die Verwendung des SaveState-Attributs vermeiden und die bestehenden Aufgaben genauer ausführen (Sie können z. B. den Zustand beibehalten, wenn der Zustand geändert wird, anstatt ihn jedes Mal zu speichern, wenn eine Methode mit dem SaveState-Attribut aufgerufen wird).This way you can avoid using the SaveState attribute and perform the persisting work more accurately (for example, you could persist the state when the state is actually changed rather than saving it each time when a method with the SaveState attribute is called).

Wenn Sie das Beispiel ausführen, wird die folgende Ausgabe angezeigt:When you run the sample, the following output is displayed. Der Client fügt zwei Elemente zum Warenkorb hinzu und erhält dann vom Dienst die Liste der Elemente im Warenkorb.The client adds two items to its shopping cart and then gets the list of items in its shopping cart from the service. Drücken Sie die EINGABETASTE in den einzelnen Konsolenfenstern, um den Dienst und den Client zu schließen.Press ENTER in each console window to shut down the service and client.

Enter the name of the product: apples  
Enter the name of the product: bananas  

Shopping cart currently contains the following items.  
apples  
bananas  
Press ENTER to shut down client  

Hinweis

Durch erneutes Erstellen des Diensts wird die Datenbankdatei überschrieben.Rebuilding the service overwrites the database file. Wenn Sie den Zustand überwachen möchten, der während mehrerer Durchläufe des Beispiels beibehalten wurde, erstellen Sie das Beispiel zwischen den einzelnen Durchläufen nicht neu.To observe state preserved across multiple runs of the sample, be sure not to rebuild the sample between runs.

So können Sie das Beispiel einrichten, erstellen und ausführenTo set up, build, and run the sample

  1. Stellen Sie sicher, dass Sie ausgeführt haben die Setupprozedur für die Windows Communication Foundation-Beispiele zum einmaligen.Ensure that you have performed the One-Time Setup Procedure for the Windows Communication Foundation Samples.

  2. Führen Sie zum Erstellen der Projektmappe die Anweisungen im Erstellen der Windows Communication Foundation-Beispiele.To build the solution, follow the instructions in Building the Windows Communication Foundation Samples.

  3. Um das Beispiel in einer einzelnen oder computerübergreifenden Konfiguration ausführen möchten, folgen Sie den Anweisungen Ausführen der Windows Communication Foundation-Beispiele.To run the sample in a single- or cross-machine configuration, follow the instructions in Running the Windows Communication Foundation Samples.

Hinweis

Um dieses Beispiel auszuführen, muss SQL Server 2005 oder SQL Express 2005 ausgeführt werden.You must be running SQL Server 2005 or SQL Express 2005 to run this sample. Wenn Sie SQL Server 2005 ausführen, müssen Sie die Konfiguration der Verbindungszeichenfolge des Diensts ändern.If you are running SQL Server 2005, you must modify the configuration of the service's connection string. Wenn Sie das Beispiel computerübergreifend ausführen, ist SQL Server nur auf dem Servercomputer erforderlich.When running cross-machine, SQL Server is only required on the server machine.

Wichtig

Die Beispiele sind möglicherweise bereits auf dem Computer installiert.The samples may already be installed on your machine. Suchen Sie nach dem folgenden Verzeichnis (Standardverzeichnis), bevor Sie fortfahren.Check for the following (default) directory before continuing.

<InstallDrive>:\WF_WCF_Samples

Wenn dieses Verzeichnis nicht vorhanden ist, fahren Sie mit Windows Communication Foundation (WCF) und Windows Workflow Foundation (WF) Samples for .NET Framework 4 aller Windows Communication Foundation (WCF) herunterladen und WFWF Beispiele.If this directory does not exist, go to Windows Communication Foundation (WCF) and Windows Workflow Foundation (WF) Samples for .NET Framework 4 to download all Windows Communication Foundation (WCF) and WFWF samples. Dieses Beispiel befindet sich im folgenden Verzeichnis.This sample is located in the following directory.

<InstallDrive>:\WF_WCF_Samples\WCF\Extensibility\Instancing\Durable

Siehe auchSee Also