Contesto dell'istanza durevoleDurable Instance Context

In questo esempio viene illustrato come personalizzare il runtime di Windows Communication Foundation (WCF) per abilitare contesti dell'istanza durevoli.This sample demonstrates how to customize the Windows Communication Foundation (WCF) runtime to enable durable instance contexts. SQL Server 2005 viene utilizzato come archivio di backup (in questo caso SQL Server 2005 Express).It uses SQL Server 2005 as its backing store (SQL Server 2005 Express in this case). Tuttavia, viene fornito anche un modo di accedere ai meccanismi di archiviazione personalizzati.However, it also provides a way to access custom storage mechanisms.

Nota

La procedura di installazione e le istruzioni di compilazione per questo esempio si trovano alla fine di questo argomento.The setup procedure and build instructions for this sample are located at the end of this topic.

In questo esempio riguarda l'estensione sia il livello del canale e il livello del modello di servizio di WCF.This sample involves extending both the channel layer and the service model layer of the WCF. È pertanto necessario comprendere i concetti sottostanti prima di entrare nei dettagli dell'implementazione.Therefore it is necessary to understand the underlying concepts before going into the implementation details.

I contesti dell'istanza durevoli si trovano spesso in situazioni del mondo reale.Durable instance contexts can be found in the real world scenarios quite often. Ad esempio, un'applicazione di carrello degli acquisti ha la possibilità di sospendere gli acquisti a metà strada e continuarli un altro giorno.A shopping cart application for example, has the ability to pause shopping halfway through and continue it on another day. Quando si visita il carrello degli acquisti il giorno successivo, il contesto originale viene ripristinato.So that when we visit the shopping cart the next day, our original context is restored. È importante notare che l'applicazione di carrello degli acquisti (sul server) non mantiene l'istanza del carrello degli acquisti mentre si è disconnessi.It is important to note that the shopping cart application (on the server) does not maintain the shopping cart instance while we are disconnected. Invece, lo stato viene salvato in un archivio durevole e viene utilizzato per generare una nuova istanza del contesto ripristinato.Instead, it persists its state into a durable storage media and uses it when constructing a new instance for the restored context. Pertanto l'istanza del servizio che può essere utile allo stesso contesto non corrisponde all'istanza precedente (ovvero, non ha lo stesso indirizzo di memoria).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).

Il contesto dell'istanza durevole è reso possibile da un piccolo protocollo che consente al client e al servizio di scambiare un ID del contesto.Durable instance context is made possible by a small protocol that exchanges a context ID between the client and service. Questo ID del contesto viene creato sul client e trasmesso al servizio.This context ID is created on the client and transmitted to the service. Quando viene creata l'istanza del servizio, il runtime del servizio tenta di caricare lo stato salvato che corrisponde a questo ID del contesto da un'archiviazione permanente (per impostazione predefinita corrisponde a un database SQL Server 2005).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). Se non ci sono stati disponibili, la nuova istanza è dotata di uno stato predefinito.If no state is available the new instance has its default state. L'implementazione del servizio utilizza un attributo personalizzato per contrassegnare le operazioni che modificano il runtime del servizio, in modo che la fase di esecuzione possa salvare l'istanza del servizio dopo averle richiamate.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.

Dalla descrizione precedente è possibile individuare i due passaggi necessari per raggiungere l'obiettivo:By the previous description, two steps can easily be distinguished to achieve the goal:

  1. Modificare il messaggio che va in transito per portare l'ID del contesto.Change the message that goes on the wire to carry the context ID.

  2. Modificare il comportamento locale del servizio per implementare la logica dell'istanza personalizzata.Change the service local behavior to implement custom instancing logic.

Poiché il primo passaggio influisce sul messaggio in transito, deve essere implementato come canale personalizzato e associato al livello del canale.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. Il secondo passaggio influisce solo sul comportamento locale del servizio e pertanto può essere implementato estendendo vari punti di estensibilità del servizio.The latter only affects the service local behavior and therefore can be implemented by extending several service extensibility points. Nelle prossime sezioni verranno illustrate tutte queste estensioni.In the next few sections, each of these extensions are discussed.

Canale InstanceContext durevoleDurable InstanceContext Channel

La prima cosa da notare è l'estensione del livello del canale.The first thing to look at is a channel layer extension. Il primo passaggio per creare un canale personalizzato consiste nel decidere la struttura della comunicazione del canale.The first step in writing a custom channel is to decide the communication structure of the channel. Poiché viene introdotto un nuovo protocollo di rete, è necessario che il canale funzioni con tutti gli altri canali dello stack di canali.As a new wire protocol is being introduced the channel should work with almost any other channel in the channel stack. È pertanto necessario che supporti tutti i modelli di scambio dei messaggi.Therefore it should support all the message exchange patterns. Tuttavia, la funzionalità principale del canale rimane la stessa, indipendentemente dalla struttura della comunicazione.However, the core functionality of the channel is the same regardless of its communication structure. Più specificamente, deve scrivere l'ID del contesto ai messaggi dal client e leggere questo ID del contesto dai messaggi dal servizio, per poi passarlo ai livelli superiori.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. Per questa ragione, viene creata una classe DurableInstanceContextChannelBase che rappresenta la classe di base astratta per tutte le implementazioni dei canali del contesto dell'istanza durevole.Because of that, a DurableInstanceContextChannelBase class is created that acts as the abstract base class for all durable instance context channel implementations. Questa classe contiene le funzioni comuni di gestione del computer di stato e due membri protetti per applicare e leggere le informazioni di contesto a e da i messaggi.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)  
  {  
    //…              
  }  
}  

Questi due metodi si avvalgono delle implementazioni IContextManager per scrivere e leggere l'ID del contesto a o da il messaggio.These two methods make use of IContextManager implementations to write and read the context ID to or from the message. (IContextManager è un'interfaccia personalizzata utilizzata per definire il contratto per tutti i gestori del contesto). Il canale può includere l'ID del contesto in un'intestazione SOAP personalizzata o in un'intestazione HTTP del cookie.(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. Ogni implementazione dei gestori di contesto eredita dalla classe ContextManagerBase che contiene le funzionalità comuni per tutti i gestori del contesto.Each context manager implementation inherits from the ContextManagerBase class that contains the common functionality for all context managers. Il metodo GetContextId in questa classe viene utilizzato per originare l'ID del contesto dal client.The GetContextId method in this class is used to originate the context ID from the client. Quando un ID del contesto viene originato per la prima volta, questo metodo lo salva in un file di testo il cui nome è costituito dall'indirizzo endpoint remoto (i caratteri del nome file non validi degli URI tipici sono sostituiti con il carattere @).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).

In un secondo momento, quando l'ID del contesto è necessario allo stesso endpoint remoto, il metodo verifica se esiste un file appropriato.Later when the context ID is required for the same remote endpoint, it checks whether an appropriate file exists. Se esiste, il metodo legge l'ID del contesto e termina.If it does, it reads the context ID and returns. In caso contrario, restituisce un ID del contesto appena generato e lo salva in un file.Otherwise it returns a newly generated context ID and saves it to a file. Nella configurazione predefinita, questi file sono posizionati in una directory chiamata ContextStore che risiede nella directory temp dell'utente corrente.With the default configuration, these files are placed in a directory called ContextStore, which resides in the current user’s temp directory. Questo percorso è tuttavia configurabile tramite l'elemento di associazione.However this location is configurable using the binding element.

Il meccanismo utilizzato per trasportare l'ID del contesto è configurabile.The mechanism used to transport the context ID is configurable. Può essere scritto nell'intestazione HTTP del cookie o in un'intestazione SOAP personalizzata.It could be either written to the HTTP cookie header or to a custom SOAP header. Se si utilizza l'intestazione SOAP personalizzata, è possibile utilizzare questo protocollo con i protocolli non HTTP (ad esempio, TCP o Named pipe).The custom SOAP header approach makes it possible to use this protocol with non-HTTP protocols (for example, TCP or Named Pipes). Le due classi MessageHeaderContextManager e HttpCookieContextManager implementano queste due opzioni.There are two classes, namely MessageHeaderContextManager and HttpCookieContextManager, which implement these two options.

Entrambi scrivono l'ID del contesto nel messaggio in modo appropriato.Both of them write the context ID to the message appropriately. Ad esempio, la classe MessageHeaderContextManager lo scrive in un'intestazione SOAP del metodo WriteContext.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);  
}   

I metodi ApplyContext e ReadContextId della classe DurableInstanceContextChannelBase richiamano rispettivamente IContextManager.ReadContext e IContextManager.WriteContext.Both the ApplyContext and ReadContextId methods in the DurableInstanceContextChannelBase class invoke the IContextManager.ReadContext and IContextManager.WriteContext, respectively. Questi gestori del contesto non vengono creati direttamente dalla classe DurableInstanceContextChannelBase.However, these context managers are not directly created by the DurableInstanceContextChannelBase class. Viene utilizzata la classe ContextManagerFactory per eseguire quel processo.Instead it uses the ContextManagerFactory class to do that job.

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

Il metodo ApplyContext viene richiamato dai canali di invio.The ApplyContext method is invoked by the sending channels. Inserisce l'ID del contesto nei messaggi in uscita.It injects the context ID to the outgoing messages. Il metodo ReadContextId viene richiamato dai canali di ricezione.The ReadContextId method is invoked by the receiving channels. Questo metodo assicura che l'ID del contesto sia disponibile nei messaggi in arrivo e lo aggiunge alla raccolta Properties della classe Message.This method ensures that the context ID is available in the incoming messages and adds it to the Properties collection of the Message class. Genera inoltre un'eccezione CommunicationException in caso di errore nella lettura dell'ID del contesto, facendo in modo che il canale venga interrotto.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);  

Prima di procedere, è importante comprendere l'utilizzo della raccolta Properties nella classe Message.Before proceeding, it is important to understand the usage of the Properties collection in the Message class. In genere, questa raccolta Properties viene utilizzata quando si passano dati dai livelli inferiore a quelli superiori dal livello del canale.Typically, this Properties collection is used when passing data from lower to the upper levels from the channel layer. In questo modo i dati desiderati possono essere forniti ai livelli superiori in modo coerente, indipendentemente dai dettagli del protocollo.This way the desired data can be provided to the upper levels in a consistent manner regardless of the protocol details. In altre parole, il livello del canale può inviare e ricevere l'ID del contesto come intestazione SOAP o come intestazione HTTP del cookie.In other words, the channel layer can send and receive the context ID either as a SOAP header or a HTTP cookie header. Ma non è necessario che i livelli superiori conoscano questi dettagli, perché il livello del canale rende disponibili queste informazioni nella raccolta Properties.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.

Una volta sistemata la classe DurableInstanceContextChannelBase tutte e dieci le interfacce necessarie (IOutputChannel, IInputChannel, IOutputSessionChannel, IInputSessionChannel, IRequestChannel, IReplyChannel, IRequestSessionChannel, IReplySessionChannel, IDuplexChannel, IDuplexSessionChannel) devono essere implementate.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. Queste interfacce presentano tutti i modelli di scambio di messaggi disponibili (datagramma, simplex, duplex e le varianti con sessione).They resemble every available message exchange pattern (datagram, simplex, duplex and their sessionful variants). Ognuna di queste implementazioni eredita la classe di base descritta precedentemente e chiama i metodi ApplyContext e ReadContexId in modo appropriato.Each of these implementations inherit the base class previously described and calls ApplyContext and ReadContexId appropriately. Ad esempio, DurableInstanceContextOutputChannel, il quale implementa l'interfaccia IOutputChannel, chiama il metodo ApplyContext da ogni metodo che invia i messaggi.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);  
    //…  
}   

Invece DurableInstanceContextInputChannel, il quale implementa l'interfaccia IInputChannel, chiama il metodo ReadContextId da ogni metodo che riceve i messaggi.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;  
}  

Tranne che in questi casi, queste implementazioni del canale delegano le chiamate al metodo al canale sottostante nello stack di canali.Apart from this, these channel implementations delegate the method invocations to the channel below them in the channel stack. Tuttavia, le varianti con sessione hanno una logica di base per assicurarsi che l'ID del contesto venga inviato e letto solo per il primo messaggio che crea la sessione.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;  
}  

Queste implementazioni del canale vengono quindi aggiunti al runtime di canale WCF per il DurableInstanceContextBindingElement classe e DurableInstanceContextBindingElementSection classe in modo appropriato.These channel implementations are then added to the WCF channel runtime by the DurableInstanceContextBindingElement class and DurableInstanceContextBindingElementSection class appropriately. Vedere il HttpCookieSession documentazione di esempio per ulteriori informazioni su elementi di associazione e sezioni di elemento di associazione del canale.See the HttpCookieSession channel sample documentation for more details about binding elements and binding element sections.

Estensioni del livello del modello di servizioService Model Layer Extensions

Ora che l'ID del contesto è stato trasferito utilizzando il livello del canale, il comportamento del servizio può essere implementato per personalizzare la creazione di istanze.Now that the context ID has traveled through the channel layer, the service behavior can be implemented to customize the instantiation. In questo esempio, viene utilizzata una gestione archivi per caricare e salvare lo stato a o da l'archivio permanente.In this sample, a storage manager is used to load and save state from or to the persistent store. Come spiegato in precedenza, questo esempio fornisce una gestione archivi che utilizza SQL Server 2005 come archivio di backup.As explained previously, this sample provides a storage manager that uses SQL Server 2005 as its backing store. È tuttavia possibile aggiungere meccanismi di archiviazione personalizzati a questa estensione.However, it is also possible to add custom storage mechanisms to this extension. Per fare ciò, viene dichiarata un'interfaccia pubblica che deve essere implementata da tutte le gestioni archivi.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);  
}  

La classe SqlServerStorageManager contiene l'implementazione IStorageManager predefinita.The SqlServerStorageManager class contains the default IStorageManager implementation. Nel metodo SaveInstance l'oggetto specificato viene serializzato utilizzando XmlSerializer e viene salvato nel database SQL Server.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();  
        }  
    }  
}  

Nel metodo GetInstance i dati serializzati vengono letti per un ID del contesto specificato e l'oggetto costruito da questo contesto viene restituito al chiamante.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;  
    }  
}  

Non è necessario che gli utenti di queste gestioni archivi creino direttamente un'istanza.Users of these storage managers are not supposed to instantiate them directly. Possono utilizzare la classe StorageManagerFactory che astrae dai dettagli della creazione della gestione archivi.They use the StorageManagerFactory class, which abstracts from the storage manager creation details. Questa classe ha uno membro statico, GetStorageManager, che crea un'istanza di un tipo di gestione archivi specificato.This class has one static member, GetStorageManager, which creates an instance of a given storage manager type. Se il parametro di tipo è null, questo metodo crea un'istanza della classe SqlServerStorageManager predefinita e la restituisce.If the type parameter is null, this method creates an instance of the default SqlServerStorageManager class and returns it. Convalida inoltre il tipo specificato per assicurarsi che implementi l'interfaccia IStorageManager.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;  
}                  
}   

L'infrastruttura necessaria per leggere e scrivere istanze dall'archiviazione permanente è implementata.The necessary infrastructure to read and write instances from the persistent storage is implemented. È ora necessario eseguire i passaggi necessari per modificare il comportamento del servizio.Now the necessary steps to change the service behavior have to be taken.

Come primo passaggio di questo processo è necessario salvare l'ID del contesto che è stato trasferito sul InstanceContext attuale utilizzando il livello del canale.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 è un componente di runtime che funge da collegamento tra il dispatcher WCF e l'istanza del servizio.InstanceContext is a runtime component that acts as the link between the WCF dispatcher and the service instance. Può essere utilizzato per fornire stati e comportamenti aggiuntivi all'istanza del servizio.It can be used to provide additional state and behavior to the service instance. È essenziale, perché nella comunicazione con sessione l'ID del contesto viene inviato solo con il primo messaggio.This is essential because in sessionful communication the context ID is sent only with the first message.

WCF consente di estendere il componente del runtime InstanceContext aggiungendo un nuovo stato e il comportamento tramite il modello di oggetti estensibili.WCF allows extending its InstanceContext runtime component by adding a new state and behavior using its extensible object pattern. Questo modello viene utilizzato in WCF per estendere le classi runtime esistenti con nuove funzionalità oppure per aggiungere nuove funzionalità di stato a un oggetto.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. Sono disponibili tre interfacce nel modello di oggetti estensibili: IExtensibleObject<T >, IExtension<T > e IExtensionCollection<T >:There are three interfaces in the extensible object pattern - IExtensibleObject<T>, IExtension<T>, and IExtensionCollection<T>:

  • Il IExtensibleObject<T > viene implementata dagli oggetti che consentono le estensioni che personalizzano le proprie funzionalità.The IExtensibleObject<T> interface is implemented by objects that allow extensions that customize their functionality.

  • Il IExtension<T > viene implementata dagli oggetti che sono estensioni di classi di tipo T.The IExtension<T> interface is implemented by objects that are extensions of classes of type T.

  • Il IExtensionCollection<T > interfaccia è una raccolta di IExtensions che consente di recuperare IExtensions in base al relativo tipo.The IExtensionCollection<T> interface is a collection of IExtensions that allows for retrieving IExtensions by their type.

È pertanto necessario creare una classe InstanceContextExtension che implementi l'interfaccia IExtension e definisca lo stato necessario per salvare l'ID del contesto.Therefore an InstanceContextExtension class should be created that implements the IExtension interface and defines the required state to save the context ID. Questa classe fornisce inoltre lo stato per memorizzare la gestione archivi utilizzata.This class also provides the state to hold the storage manager being used. Una volta salvato il nuovo stato, non sarà possibile modificarlo.Once the new state is saved, it should not be possible to modify it. Lo stato viene pertanto fornito e salvato nell'istanza che viene generata in quel momento e vi si potrà accedere soltanto utilizzando le proprietà di sola lettura.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; }              
}   

La classe InstanceContextInitializer implementa l'interfaccia IInstanceContextInitializer e aggiunge l'estensione del contesto di istanza alla raccolta di estensioni dell'InstanceContext che viene generata.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);  
}  

Come descritto precedentemente, l'ID del contesto viene letto dalla raccolta Properties della classe Message e passato al costruttore della classe dell'estensione.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. Questo dimostra che le informazioni possono essere scambiate tra i livelli in modo coerente.This demonstrates how information can be exchanged between the layers in a consistent manner.

Il successivo passaggio consiste nell'eseguire l'override del processo di creazione dell'istanza di servizio.The next important step is overriding the service instance creation process. WCF consente l'implementazione del comportamento di creazione di istanze personalizzati e la relativa associazione al runtime utilizzando l'interfaccia IInstanceProvider.WCF allows implementing custom instantiation behaviors and hooking them up to the runtime using the IInstanceProvider interface. La nuova classe InstanceProvider viene implementata per eseguire quel processo.The new InstanceProvider class is implemented to do that job. Il tipo di servizio previsto dal provider di istanze viene accettato nel costruttore.In the constructor the service type expected from the instance provider is accepted. In un secondo momento viene utilizzato per creare nuove istanze.Later this is used to create new instances. Nell'implementazione GetInstance un'istanza di una gestione archivi viene creata cercando un'istanza persistente.In the GetInstance implementation an instance of a storage manager is created looking for a persisted instance. Se restituisce null, viene creata una nuova istanza del tipo di servizio e viene restituita al chiamante.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;  
}  

Il successivo passaggio consiste nell'installare le classi InstanceContextExtension, InstanceContextInitializer e InstanceProvider nel runtime del modello di servizi.The next important step is to install the InstanceContextExtension, InstanceContextInitializer and InstanceProvider classes into the service model runtime. È possibile utilizzare un attributo personalizzato per contrassegnare le classi di implementazione del servizio perché installino il comportamento.A custom attribute could be used to mark the service implementation classes to install the behavior. DurableInstanceContextAttribute contiene l'implementazione per questo attributo e implementa l'interfaccia IServiceBehavior per estendere l'intero runtime del servizio.The DurableInstanceContextAttribute contains the implementation for this attribute and it implements the IServiceBehavior interface to extend the entire service runtime.

Questa classe è dotata di una proprietà che accetta il tipo della gestione archivi da utilizzare.This class has a property that accepts the type of the storage manager to be used. In questo modo l'implementazione consente agli utenti di specificare la propria implementazione IStorageManager come parametro di questo attributo.In this way the implementation enables the users to specify their own IStorageManager implementation as parameter of this attribute.

Nell'implementazione ApplyDispatchBehavior viene verificato il InstanceContextMode dell'attributo ServiceBehavior attuale.In the ApplyDispatchBehavior implementation the InstanceContextMode of the current ServiceBehavior attribute is being verified. Se questa proprietà è impostata su Singleton, non è possibile abilitare la creazione di istanze durevoli e viene generata un'eccezione InvalidOperationException per notificare l'host.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"));  
}  

Dopodiché le istanze della gestione archivi, dell'inizializzatore del contesto dell'istanza e del provider di istanze vengono creati e installati nel DispatchRuntime creato per ogni endpoint.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;  
        }  
    }  
}  

Per riassumere, questo esempio ha prodotto un canale che abilita il protocollo di rete personalizzato per lo scambio di ID del contesto personalizzati e sovrascrive il comportamento di istanza predefinito per caricare le istanze dall'archivio permanente.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.

Rimane da trovare un modo per salvare l'istanza del servizio nell'archivio permanente.What is left is a way to save the service instance to the persistent storage. Come illustrato precedentemente, esiste già la funzionalità necessaria per salvare lo stato in un'implementazione IStorageManager.As discussed previously, there is already the required functionality to save the state in an IStorageManager implementation. È ora necessario integrare questa funzionalità con il runtime WCF.We now must integrate this with the WCF runtime. È necessario un altro attributo applicabile ai metodi della classe di implementazione del servizio.Another attribute is required that is applicable to the methods in the service implementation class. Questo attributo va applicato ai metodi che modificano lo stato dell'istanza del servizio.This attribute is supposed to be applied to the methods that change the state of the service instance.

La classe SaveStateAttribute implementa questa funzionalità.The SaveStateAttribute class implements this functionality. Implementa inoltre IOperationBehavior (classe) per modificare il runtime WCF per ogni operazione.It also implements IOperationBehavior class to modify the WCF runtime for each operation. Quando un metodo è contrassegnato con questo attributo, il runtime WCF richiama il ApplyBehavior il metodo appropriato DispatchOperation in fase di costruzione.When a method is marked with this attribute, the WCF runtime invokes the ApplyBehavior method while the appropriate DispatchOperation is being constructed. In questa implementazione del metodo c'è una sola riga di codice:In this method implementation there is single line of code:

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

Questa istruzione crea un'istanza di tipo OperationInvoker e la assegna alla proprietà Invoker dell'elemento DispatchOperation che viene generato.This instruction creates an instance of OperationInvoker type and assigns it to the Invoker property of the DispatchOperation being constructed. La classe OperationInvoker è un wrapper per l'invoker dell'operazione predefinita creato per DispatchOperation.The OperationInvoker class is a wrapper for the default operation invoker created for the DispatchOperation. Questa classe implementa l'interfaccia IOperationInvoker.This class implements the IOperationInvoker interface. Nell'implementazione del metodo Invoke la chiamata al metodo effettiva viene delegata all'invoker dell'operazione interna.In the Invoke method implementation the actual method invocation is delegated to the inner operation invoker. Tuttavia, prima di restituire i risultati la gestione archivi in InstanceContext viene utilizzata per salvare l'istanza del servizio.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;  

Utilizzo dell'estensioneUsing the Extension

Sia il livello del canale e le estensioni del livello di servizio modello vengono eseguite e possono essere utilizzate nelle applicazioni WCF.Both the channel layer and service model layer extensions are done and they can now be used in WCF applications. È necessario che i servizi aggiungano il canale allo stack di canali utilizzando un'associazione personalizzata e che contrassegnino quindi le classi di implementazione del servizio con gli attributi appropriati.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)  
     {  
         //…  
     }  
//…  
 }  

È necessario che le applicazioni client aggiungano DurableInstanceContextChannel allo stack di canali utilizzando un'associazione personalizzata.Client applications must add the DurableInstanceContextChannel into the channel stack using a custom binding. Per configurare in modo dichiarativo il canale nel file di configurazione la sezione dell'elemento di associazione deve essere aggiunta alla raccolta delle estensioni degli elementi di associazione.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>  

È ora possibile utilizzare l'elemento di associazione con un'associazione personalizzata, proprio come gli altri elementi di associazione standard: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>  

ConclusioneConclusion

In questo esempio viene illustrato come creare un canale del protocollo personalizzato e come personalizzare il comportamento del servizio per abilitarlo.This sample showed how to create a custom protocol channel and how to customize the service behavior to enable it.

L'estensione può essere ulteriormente migliorata consentendo agli utenti di specificare l'implementazione IStorageManager utilizzando una sezione di configurazione.The extension can be further improved by letting users specify the IStorageManager implementation using a configuration section. In questo modo è possibile modificare l'archivio di backup senza ricompilare il codice del servizio.This makes it possible to modify the backing store without recompiling the service code.

È inoltre possibile tentare di implementare una classe (ad esempio, StateBag) che incapsuli lo stato dell'istanza.Furthermore you could try to implement a class (for example, StateBag), which encapsulates the state of the instance. Quella classe è responsabile di salvare in modo permanente lo stato quando viene modificato.That class is responsible for persisting the state whenever it changes. In questo modo è possibile evitare di utilizzare l'attributo SaveState ed eseguire più accuratamente il lavoro di archiviazione permanente (ad esempio, è possibile salvare in modo permanente lo stato solo quando viene davvero modificato piuttosto che salvarlo ogni volta che viene chiamato un metodo con l'attributo SaveState).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).

Quando si esegue l'esempio, viene visualizzato l'output seguente:When you run the sample, the following output is displayed. Il client aggiunge due elementi al carrello degli acquisti e ottiene l'elenco di elementi nel carrello degli acquisti dal servizio.The client adds two items to its shopping cart and then gets the list of items in its shopping cart from the service. Premere INVIO in tutte le finestre della console per arrestare il servizio e il client.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  

Nota

La ricompilazione del servizio si sovrascrive il file di database.Rebuilding the service overwrites the database file. Per osservare lo stato salvato in modo permanente in più esecuzioni dell'esempio, assicurarsi di non ricompilare l'esempio tra esecuzioni.To observe state preserved across multiple runs of the sample, be sure not to rebuild the sample between runs.

Per impostare, compilare ed eseguire l'esempioTo set up, build, and run the sample

  1. Assicurarsi di avere eseguito la procedura di installazione singola per gli esempi di Windows Communication Foundation.Ensure that you have performed the One-Time Setup Procedure for the Windows Communication Foundation Samples.

  2. Per compilare la soluzione, seguire le istruzioni in compilazione degli esempi di Windows Communication Foundation.To build the solution, follow the instructions in Building the Windows Communication Foundation Samples.

  3. Per eseguire l'esempio in una configurazione singola o tra computer, seguire le istruzioni in esegue gli esempi di Windows Communication Foundation.To run the sample in a single- or cross-machine configuration, follow the instructions in Running the Windows Communication Foundation Samples.

Nota

È necessario che SQL Server 2005 o SQL Express 2005 siano in esecuzione per eseguire questo esempio.You must be running SQL Server 2005 or SQL Express 2005 to run this sample. Se è in esecuzione SQL Server 2005, è necessario modificare la configurazione della stringa di connessione del servizio.If you are running SQL Server 2005, you must modify the configuration of the service's connection string. Quando si esegue tra più computer, SQL Server è necessario solo sul server.When running cross-machine, SQL Server is only required on the server machine.

Importante

È possibile che gli esempi siano già installati nel computer.The samples may already be installed on your machine. Verificare la directory seguente (impostazione predefinita) prima di continuare.Check for the following (default) directory before continuing.

<InstallDrive>:\WF_WCF_Samples

Se questa directory non esiste, andare al Windows Communication Foundation (WCF) e gli esempi di Windows Workflow Foundation (WF) per .NET Framework 4 per scaricare tutti i Windows Communication Foundation (WCF) e WFWF esempi.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. Questo esempio si trova nella directory seguente.This sample is located in the following directory.

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

Vedere ancheSee Also