Udostępnij za pośrednictwem


Kontekst niezawodnej instancji

W przykładzie Durable pokazano, jak dostosować środowisko uruchomieniowe programu Windows Communication Foundation (WCF) w celu włączenia trwałych kontekstów wystąpień. Używa on programu SQL Server 2005 jako magazynu zapasowego (w tym przypadku programu SQL Server 2005 Express). Jednak zapewnia również dostęp do niestandardowych mechanizmów magazynowania.

Uwaga

Procedura instalacji i instrukcje kompilacji dla tego przykładu znajdują się na końcu tego artykułu.

Ten przykład obejmuje rozszerzenie warstwy kanału i warstwy modelu usługi WCF. W związku z tym należy zrozumieć podstawowe pojęcia przed przejściem do szczegółów implementacji.

Konteksty wystąpienia trwałego można znaleźć w rzeczywistych scenariuszach dość często. Aplikacja koszyka na zakupy ma na przykład możliwość wstrzymania zakupów w połowie drogi i kontynuowania jej w innym dniu. Więc kiedy odwiedzamy koszyk następnego dnia, nasz oryginalny kontekst zostanie przywrócony. Należy pamiętać, że aplikacja koszyka na zakupy (na serwerze) nie obsługuje wystąpienia koszyka zakupów, gdy jesteśmy rozłączni. Zamiast tego utrzymuje swój stan na trwałym nośniku magazynu i używa go podczas konstruowania nowego wystąpienia dla przywróconego kontekstu. Dlatego wystąpienie usługi, które może obsługiwać ten sam kontekst, nie jest takie samo jak poprzednie wystąpienie (czyli nie ma tego samego adresu pamięci).

Kontekst wystąpienia trwałego jest możliwy za pomocą małego protokołu, który wymienia identyfikator kontekstu między klientem a usługą. Ten identyfikator kontekstu jest tworzony na kliencie i przesyłany do usługi. Po utworzeniu wystąpienia usługi środowisko uruchomieniowe usługi próbuje załadować stan utrwalonego, który odpowiada temu identyfikatorowi kontekstu z magazynu trwałego (domyślnie jest to baza danych programu SQL Server 2005). Jeśli stan nie jest dostępny, nowe wystąpienie ma stan domyślny. Implementacja usługi używa atrybutu niestandardowego do oznaczania operacji, które zmieniają stan implementacji usługi, aby środowisko uruchomieniowe może zapisać wystąpienie usługi po ich wywołaniu.

W poprzednim opisie można łatwo odróżnić dwa kroki, aby osiągnąć cel:

  1. Zmień komunikat, który przechodzi do przewodu, aby nosić identyfikator kontekstu.
  2. Zmień zachowanie lokalne usługi, aby zaimplementować niestandardową logikę stancingu.

Ponieważ pierwszy na liście wpływa na komunikaty w sieci, należy go zaimplementować jako kanał niestandardowy i podłączyć do warstwy kanału. Ten ostatni ma wpływ tylko na lokalne zachowanie usługi i dlatego można zaimplementować przez rozszerzenie kilku punktów rozszerzalności usługi. W kilku następnych sekcjach omówiono każde z tych rozszerzeń.

Kanał Durable InstanceContext

Pierwszą rzeczą, na którą należy zwrócić uwagę, jest rozszerzenie warstwy kanału. Pierwszym krokiem podczas pisania kanału niestandardowego jest podjęcie decyzji o strukturze komunikacyjnej kanału. W miarę wprowadzania nowego protokołu przewodowego kanał powinien współpracować z niemal każdym innym kanałem w stosie kanału. W związku z tym powinna obsługiwać wszystkie wzorce wymiany komunikatów. Jednak podstawowe funkcje kanału są takie same niezależnie od struktury komunikacji. Mówiąc dokładniej, od klienta powinien on zapisywać identyfikator kontekstu do komunikatów i z usługi powinien odczytać ten identyfikator kontekstu z komunikatów i przekazać go do wyższych poziomów. Z tego powodu tworzona DurableInstanceContextChannelBase jest klasa, która działa jako abstrakcyjna klasa bazowa dla wszystkich trwałych implementacji kanału kontekstowego wystąpienia. Ta klasa zawiera typowe funkcje zarządzania maszynami stanowymi i dwie chronione elementy członkowskie do zastosowania i odczytują informacje kontekstowe do i z komunikatów.

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

Te dwie metody wykorzystują IContextManager implementacje do zapisu i odczytywania identyfikatora kontekstu do lub z komunikatu. (IContextManager to interfejs niestandardowy służący do definiowania kontraktu dla wszystkich menedżerów kontekstu). Kanał może zawierać identyfikator kontekstu w niestandardowym nagłówku protokołu SOAP lub w nagłówku pliku cookie HTTP. Każda implementacja menedżera kontekstu dziedziczy z ContextManagerBase klasy, która zawiera typowe funkcje dla wszystkich menedżerów kontekstu. Metoda GetContextId w tej klasie służy do uzyskiwania identyfikatora kontekstu z klienta. Gdy identyfikator kontekstu jest tworzony po raz pierwszy, ta metoda zapisuje go w pliku tekstowym, którego nazwa jest konstruowana przez zdalny adres punktu końcowego (nieprawidłowe znaki nazwy pliku w typowych identyfikatorach URI są zastępowane znakami @).

Później, gdy identyfikator kontekstu jest wymagany dla tego samego zdalnego punktu końcowego, sprawdza, czy istnieje odpowiedni plik. Jeśli tak, odczytuje identyfikator kontekstu i zwraca. W przeciwnym razie zwraca nowo wygenerowany identyfikator kontekstu i zapisuje go w pliku. W przypadku konfiguracji domyślnej te pliki są umieszczane w katalogu o nazwie ContextStore, który znajduje się w katalogu tymczasowym bieżącego użytkownika. Jednak ta lokalizacja jest konfigurowalna przy użyciu elementu powiązania.

Mechanizm używany do transportu identyfikatora kontekstu można skonfigurować. Można go zapisać w nagłówku pliku cookie HTTP lub w niestandardowym nagłówku protokołu SOAP. Niestandardowe podejście nagłówka protokołu SOAP umożliwia użycie tego protokołu z protokołami innych niż HTTP (na przykład TCP lub nazwanymi potokami). Istnieją dwie klasy, a mianowicie MessageHeaderContextManager i HttpCookieContextManager, które implementują te dwie opcje.

Oba te elementy odpowiednio zapisują identyfikator kontekstu do wiadomości. Na przykład MessageHeaderContextManager klasa zapisuje ją w nagłówku SOAP w metodzie WriteContext .

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

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

  message.Headers.Add(contextHeader);
}

ApplyContext Metody i ReadContextId w DurableInstanceContextChannelBase klasie wywołują IContextManager.ReadContext odpowiednio metody i i IContextManager.WriteContext. Jednak te menedżery kontekstu nie są tworzone bezpośrednio przez klasę DurableInstanceContextChannelBase . Zamiast tego używa ContextManagerFactory klasy do wykonania tego zadania.

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

Metoda ApplyContext jest wywoływana przez kanały wysyłające. Wprowadza identyfikator kontekstu do wychodzących komunikatów. Metoda ReadContextId jest wywoływana przez kanały odbierające. Ta metoda gwarantuje, że identyfikator kontekstu jest dostępny w komunikatach przychodzących i dodaje go do Properties kolekcji Message klasy. Zgłasza CommunicationException również wyjątek w przypadku niepowodzenia odczytania identyfikatora kontekstu, co powoduje przerwanie kanału.

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

Przed kontynuowaniem ważne jest, aby zrozumieć użycie kolekcji Properties w Message klasie. Zazwyczaj ta Properties kolekcja jest używana podczas przekazywania danych z niższych do wyższych poziomów z warstwy kanału. W ten sposób żądane dane można dostarczyć do wyższych poziomów w spójny sposób niezależnie od szczegółów protokołu. Innymi słowy, warstwa kanału może wysyłać i odbierać identyfikator kontekstu jako nagłówek protokołu SOAP lub nagłówek pliku cookie HTTP. Nie jest jednak konieczne, aby górne poziomy wiedziały o tych szczegółach, ponieważ warstwa kanału udostępnia te informacje w kolekcji Properties .

Teraz z klasą DurableInstanceContextChannelBase należy zaimplementować wszystkie dziesięć niezbędnych interfejsów (IOutputChannel, IInputChannel, IOutputSessionChannel, IInputSessionChannel, IRequestChannel, IReplyChannel, IRequestSessionChannel, IReplySessionChannel, IDuplexSessionChannel). Przypominają one każdy dostępny wzorzec wymiany komunikatów (datagram, simplex, duplex i ich warianty sesji). Każda z tych implementacji dziedziczy wcześniej opisaną klasę bazową i wywołuje ApplyContext i ReadContextId odpowiednio. Na przykład DurableInstanceContextOutputChannel — który implementuje interfejs IOutputChannel — wywołuje metodę ApplyContext z każdej metody, która wysyła komunikaty.

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

Z drugiej strony DurableInstanceContextInputChannel — która implementuje IInputChannel interfejs — wywołuje metodę ReadContextId w każdej metodzie, która odbiera komunikaty.

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

Oprócz tego te implementacje kanału delegują wywołania metody do kanału poniżej w stosie kanału. Jednak warianty sesji mają podstawową logikę, aby upewnić się, że identyfikator kontekstu jest wysyłany i jest tylko do odczytu dla pierwszego komunikatu, który powoduje utworzenie sesji.

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

Te implementacje kanału są następnie dodawane do środowiska uruchomieniowego kanału WCF odpowiednio przez klasę DurableInstanceContextBindingElement i DurableInstanceContextBindingElementSection klasę. Zobacz przykładową dokumentację kanału HttpCookieSession , aby uzyskać więcej informacji na temat powiązań elementów i sekcji elementów powiązania.

Rozszerzenia warstwy modelu usługi

Teraz, gdy identyfikator kontekstu przeszedł przez warstwę kanału, można zaimplementować zachowanie usługi w celu dostosowania wystąpienia. W tym przykładzie menedżer magazynu służy do ładowania i zapisywania stanu z lub do magazynu trwałego. Jak wyjaśniono wcześniej, ten przykład udostępnia menedżera magazynu, który używa programu SQL Server 2005 jako magazynu zapasowego. Jednak istnieje również możliwość dodania niestandardowych mechanizmów magazynu do tego rozszerzenia. Aby to zrobić, zadeklarowano interfejs publiczny, który musi zostać zaimplementowany przez wszystkich menedżerów magazynu.

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

Klasa SqlServerStorageManager zawiera domyślną IStorageManager implementację. W swojej SaveInstance metodzie dany obiekt jest serializowany przy użyciu klasy XmlSerializer i jest zapisywany w bazie danych programu SQL Server.

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();
        }
    }
}

W metodzie GetInstance dane serializowane są odczytywane dla danego identyfikatora kontekstu, a obiekt skonstruowany z niego jest zwracany do obiektu wywołującego.

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;
    }
}

Użytkownicy tych menedżerów magazynu nie powinni bezpośrednio utworzyć ich wystąpienia. Używają StorageManagerFactory klasy, która abstrahuje od szczegółów tworzenia menedżera magazynu. Ta klasa ma jeden statyczny element członkowski , GetStorageManagerktóry tworzy wystąpienie danego typu menedżera magazynu. Jeśli parametr typu to null, ta metoda tworzy wystąpienie klasy domyślnej SqlServerStorageManager i zwraca je. Weryfikuje również dany typ, aby upewnić się, że implementuje IStorageManager interfejs.

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;
}
}

Zaimplementowano infrastrukturę niezbędną do odczytu i zapisu wystąpień z magazynu trwałego. Teraz należy wykonać niezbędne kroki, aby zmienić zachowanie usługi.

W pierwszym kroku tego procesu musimy zapisać identyfikator kontekstu, który przeszedł przez warstwę kanału do bieżącego wystąpieniaContext. InstanceContext to składnik środowiska uruchomieniowego, który działa jako połączenie między dyspozytorem WCF a wystąpieniem usługi. Może służyć do zapewnienia dodatkowego stanu i zachowania wystąpienia usługi. Jest to niezbędne, ponieważ w sesji komunikacji identyfikator kontekstu jest wysyłany tylko przy użyciu pierwszej wiadomości.

Program WCF umożliwia rozszerzenie składnika środowiska uruchomieniowego InstanceContext przez dodanie nowego stanu i zachowania przy użyciu rozszerzalnego wzorca obiektu. Rozszerzalny wzorzec obiektu jest używany w programie WCF do rozszerzania istniejących klas środowiska uruchomieniowego o nowe funkcje lub dodawania nowych funkcji stanu do obiektu. Istnieją trzy interfejsy we wzorcu obiektu rozszerzalnego — IExtensibleObject<T>, IExtension<T i IExtensionCollection<T>>:

  • Interfejs IExtensibleObject<T> jest implementowany przez obiekty, które umożliwiają rozszerzenia, które dostosowują ich funkcjonalność.

  • Interfejs IExtension<T> jest implementowany przez obiekty, które są rozszerzeniami klas typu T.

  • Interfejs IExtensionCollection<T> to kolekcja rozszerzeń IExtensions, która umożliwia pobieranie rozszerzeń IExtensions według ich typu.

W związku z tym należy utworzyć klasę InstanceContextExtension, która implementuje interfejs IExtension i definiuje wymagany stan do zapisania identyfikatora kontekstu. Ta klasa udostępnia również stan przechowywania używanego menedżera magazynu. Po zapisaniu nowego stanu nie powinno być możliwe jego zmodyfikowanie. W związku z tym stan jest udostępniany i zapisywany w wystąpieniu w momencie jego konstruowania, a następnie dostępny tylko przy użyciu właściwości tylko do odczytu.

// 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; }
}

Klasa InstanceContextInitializer implementuje interfejs IInstanceContextInitializer i dodaje rozszerzenie kontekstu wystąpienia do kolekcji Extensions tworzonego elementu InstanceContext.

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

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

Jak opisano wcześniej, identyfikator kontekstu jest odczytywany z Properties kolekcji Message klasy i przekazywany do konstruktora klasy rozszerzenia. Pokazuje to, jak można wymieniać informacje między warstwami w spójny sposób.

Następnym ważnym krokiem jest zastąpienie procesu tworzenia wystąpienia usługi. Program WCF umożliwia implementowanie niestandardowych zachowań tworzenia wystąpień i podłączanie ich do środowiska uruchomieniowego przy użyciu interfejsu IInstanceProvider. Nowa InstanceProvider klasa jest implementowana w celu wykonania tego zadania. Typ usługi oczekiwany od dostawcy wystąpienia jest akceptowany w konstruktorze. Później jest to używane do tworzenia nowych wystąpień. W implementacji GetInstance tworzone jest wystąpienie menedżera magazynu, które szuka utrwalonego wystąpienia. Jeśli zwraca nullwartość , wystąpienie nowego wystąpienia typu usługi jest tworzone i zwracane do obiektu wywołującego.

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);

    instance ??= Activator.CreateInstance(serviceType);
    return instance;
}

Następnym ważnym krokiem jest zainstalowanie InstanceContextExtensionklas , InstanceContextInitializeri InstanceProvider w środowisku uruchomieniowym modelu usługi. Atrybut niestandardowy może służyć do oznaczania klas implementacji usługi w celu zainstalowania zachowania. Zawiera DurableInstanceContextAttribute implementację tego atrybutu i implementuje IServiceBehavior interfejs w celu rozszerzenia całego środowiska uruchomieniowego usługi.

Ta klasa ma właściwość, która akceptuje typ menedżera magazynu do użycia. W ten sposób implementacja umożliwia użytkownikom określenie własnej IStorageManager implementacji jako parametru tego atrybutu.

W implementacji ApplyDispatchBehavior InstanceContextMode jest weryfikowany bieżący ServiceBehavior atrybut. Jeśli ta właściwość jest ustawiona na Singleton, włączenie trwałego stancingu nie jest możliwe i zostanie InvalidOperationException zgłoszony do powiadomienia hosta.

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

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

Po wykonaniu tych wystąpień menedżera magazynu inicjator kontekstu wystąpienia i dostawcy wystąpień zostaną utworzone i zainstalowane w utworzonym DispatchRuntime dla każdego punktu końcowego.

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;
        }
    }
}

Podsumowując do tej pory, ten przykład opracował kanał, który włączył niestandardowy protokół przewodowy dla niestandardowej wymiany identyfikatorów kontekstu, a także zastępuje domyślne zachowanie instancing w celu załadowania wystąpień z magazynu trwałego.

Pozostaje sposób zapisywania wystąpienia usługi w magazynie trwałym. Jak wspomniano wcześniej, istnieje już wymagana funkcja zapisywania stanu w implementacji IStorageManager . Teraz musimy to zintegrować ze środowiskiem uruchomieniowym WCF. Wymagany jest inny atrybut, który ma zastosowanie do metod w klasie implementacji usługi. Ten atrybut ma być stosowany do metod, które zmieniają stan wystąpienia usługi.

Klasa SaveStateAttribute implementuje tę funkcję. Implementuje również klasę IOperationBehavior , aby zmodyfikować środowisko uruchomieniowe programu WCF dla każdej operacji. Gdy metoda jest oznaczona za pomocą tego atrybutu, środowisko uruchomieniowe programu WCF wywołuje metodę ApplyBehavior podczas konstruowania odpowiedniego DispatchOperation . W tej implementacji metody istnieje jeden wiersz kodu:

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

Ta instrukcja tworzy wystąpienie OperationInvoker typu i przypisuje je do Invoker właściwości DispatchOperation konstrukcji. Klasa OperationInvoker jest otoką dla domyślnego wywoływania operacji utworzonego dla elementu DispatchOperation. Ta klasa implementuje IOperationInvoker interfejs. W implementacji Invoke metody rzeczywiste wywołanie metody jest delegowane do wewnętrznego wywołania operacji. Jednak przed zwróceniem wyników menedżer magazynu w obiekcie InstanceContext jest używany do zapisywania wystąpienia usługi.

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;

Korzystanie z rozszerzenia

Zarówno warstwy kanału, jak i rozszerzenia warstwy modelu usługi są wykonywane i mogą być teraz używane w aplikacjach WCF. Usługi muszą dodać kanał do stosu kanału przy użyciu powiązania niestandardowego, a następnie oznaczyć klasy implementacji usługi odpowiednimi atrybutami.

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

Aplikacje klienckie muszą dodać element DurableInstanceContextChannel do stosu kanału przy użyciu powiązania niestandardowego. Aby deklaratywnie skonfigurować kanał w pliku konfiguracji, sekcja elementu powiązania musi zostać dodana do kolekcji rozszerzeń elementów powiązania.

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

Teraz element powiązania może być używany z powiązaniem niestandardowym, podobnie jak inne standardowe elementy powiązania:

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

Podsumowanie

W tym przykładzie pokazano, jak utworzyć niestandardowy kanał protokołu i jak dostosować zachowanie usługi w celu jej włączenia.

Rozszerzenie można jeszcze bardziej ulepszyć, umożliwiając użytkownikom określenie IStorageManager implementacji przy użyciu sekcji konfiguracji. Dzięki temu można modyfikować magazyn zapasowy bez ponownego komkompilowania kodu usługi.

Ponadto można spróbować zaimplementować klasę (na przykład StateBag), która hermetyzuje stan wystąpienia. Ta klasa jest odpowiedzialna za utrwalanie stanu za każdym razem, gdy zmienia się. W ten sposób można uniknąć używania atrybutu SaveState i dokładniej wykonywać utrwalającą pracę (na przykład można utrwalać stan, gdy stan jest rzeczywiście zmieniany, zamiast zapisywać go za każdym razem, gdy wywoływana jest metoda z atrybutem SaveState ).

Po uruchomieniu przykładu zostaną wyświetlone następujące dane wyjściowe. Klient dodaje dwa elementy do koszyka zakupów, a następnie pobiera listę przedmiotów w koszyku zakupów z usługi. Naciśnij ENTER w każdym oknie konsoli, aby zamknąć usługę i klienta.

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

Uwaga

Ponowne kompilowanie usługi zastępuje plik bazy danych. Aby zaobserwować zachowanie stanu w wielu uruchomieniach próbki, pamiętaj, aby nie ponownie skompilować przykładu między przebiegami.

Aby skonfigurować, skompilować i uruchomić przykład

  1. Upewnij się, że wykonano procedurę instalacji jednorazowej dla przykładów programu Windows Communication Foundation.

  2. Aby skompilować rozwiązanie, postępuj zgodnie z instrukcjami w temacie Building the Windows Communication Foundation Samples (Tworzenie przykładów programu Windows Communication Foundation).

  3. Aby uruchomić przykład w konfiguracji pojedynczej lub między maszynami, postępuj zgodnie z instrukcjami w temacie Uruchamianie przykładów programu Windows Communication Foundation.

Uwaga

Aby uruchomić ten przykład, musisz uruchomić program SQL Server 2005 lub sql Express 2005. Jeśli używasz programu SQL Server 2005, musisz zmodyfikować konfigurację parametry połączenia usługi. W przypadku uruchamiania między maszynami program SQL Server jest wymagany tylko na maszynie serwera.