Delen via


Durable Instance Context

Het Durable-voorbeeld laat zien hoe u de WCF-runtime (Windows Communication Foundation) aanpast om durable instance-contexten in te schakelen. In dit geval wordt SQL Server 2005 gebruikt als back-uparchief (SQL Server 2005 Express). Het biedt echter ook een manier om toegang te krijgen tot aangepaste opslagmechanismen.

Notitie

De installatieprocedure en build-instructies voor dit voorbeeld bevinden zich aan het einde van dit artikel.

Dit voorbeeld omvat het uitbreiden van zowel de kanaallaag als de servicemodellaag van het WCF. Daarom is het noodzakelijk om de onderliggende concepten te begrijpen voordat u de implementatiedetails gaat bekijken.

Duurzame exemplaarcontexten zijn vrij vaak te vinden in de praktijkscenario's. Een winkelwagentoepassing heeft bijvoorbeeld de mogelijkheid om het winkelen halverwege te onderbreken en door te gaan op een andere dag. Dus wanneer we de winkelwagen de volgende dag bezoeken, wordt onze oorspronkelijke context hersteld. Het is belangrijk om te weten dat de winkelwagentoepassing (op de server) het winkelwagenexemplaren niet onderhoudt terwijl we de verbinding verbreken. In plaats daarvan blijft de status behouden in een duurzame opslagmedia en wordt deze gebruikt bij het maken van een nieuw exemplaar voor de herstelde context. Daarom is het service-exemplaar dat voor dezelfde context kan worden gebruikt, niet hetzelfde als het vorige exemplaar (dat wil gezegd, het heeft niet hetzelfde geheugenadres).

Duurzame exemplaarcontext wordt mogelijk gemaakt door een klein protocol dat een context-id uitwisselt tussen de client en de service. Deze context-id wordt gemaakt op de client en verzonden naar de service. Wanneer het service-exemplaar wordt gemaakt, probeert de serviceruntime de persistente status te laden die overeenkomt met deze context-id van een permanente opslag (standaard is dit een SQL Server 2005-database). Als er geen status beschikbaar is, heeft het nieuwe exemplaar de standaardstatus. De service-implementatie maakt gebruik van een aangepast kenmerk om bewerkingen te markeren die de status van de service-implementatie wijzigen, zodat de runtime het service-exemplaar kan opslaan nadat deze zijn aanroepen.

In de vorige beschrijving kunnen twee stappen eenvoudig worden onderscheiden om het doel te bereiken:

  1. Wijzig het bericht dat op de kabel wordt geplaatst om de context-id te dragen.
  2. Wijzig het lokale gedrag van de service om aangepaste instancinglogica te implementeren.

Omdat de eerste in de lijst van invloed is op de berichten op de draad, moet deze worden geïmplementeerd als een aangepast kanaal en worden gekoppeld aan de kanaallaag. De laatste is alleen van invloed op het lokale gedrag van de service en kan daarom worden geïmplementeerd door verschillende service-uitbreidbaarheidspunten uit te breiden. In de volgende secties worden deze extensies besproken.

Durable InstanceContext-kanaal

Het eerste wat u moet bekijken, is een kanaallaagextensie. De eerste stap bij het schrijven van een aangepast kanaal is het bepalen van de communicatiestructuur van het kanaal. Als er een nieuw wire-protocol wordt geïntroduceerd, moet het kanaal werken met vrijwel elk ander kanaal in de kanaalstack. Daarom moet het alle berichtuitwisselingspatronen ondersteunen. De kernfunctionaliteit van het kanaal is echter hetzelfde, ongeacht de communicatiestructuur. Met name van de client moet de context-id naar de berichten worden geschreven en van de service moet deze context-id van de berichten worden gelezen en doorgegeven aan de bovenste niveaus. Daarom wordt er een DurableInstanceContextChannelBase klasse gemaakt die fungeert als de abstracte basisklasse voor alle implementaties van een duurzaam exemplaarcontextkanaal. Deze klasse bevat de algemene statusfuncties voor machinebeheer en twee beveiligde leden om de contextinformatie van en naar berichten toe te passen en te lezen.

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

Deze twee methoden maken gebruik van IContextManager implementaties om de context-id naar of van het bericht te schrijven en te lezen. (IContextManager is een aangepaste interface die wordt gebruikt om het contract voor alle contextbeheerders te definiëren.) Het kanaal kan de context-id opnemen in een aangepaste SOAP-header of in een HTTP-cookieheader. Elke contextbeheer-implementatie neemt over van de ContextManagerBase klasse die de algemene functionaliteit voor alle contextbeheerders bevat. De GetContextId methode in deze klasse wordt gebruikt om de context-id van de client te genereren. Wanneer een context-id voor het eerst afkomstig is, wordt deze met deze methode opgeslagen in een tekstbestand waarvan de naam is samengesteld door het externe eindpuntadres (de ongeldige bestandsnaamtekens in de typische URI's worden vervangen door @ tekens).

Later wanneer de context-id is vereist voor hetzelfde externe eindpunt, wordt gecontroleerd of er een geschikt bestand bestaat. Als dit het geval is, wordt de context-id gelezen en geretourneerd. Anders wordt een zojuist gegenereerde context-id geretourneerd en opgeslagen in een bestand. Met de standaardconfiguratie worden deze bestanden in een map geplaatst met de naam ContextStore, die zich in de tijdelijke map van de huidige gebruiker bevindt. Deze locatie kan echter worden geconfigureerd met behulp van het bindingselement.

Het mechanisme dat wordt gebruikt om de context-id te transporteren, kan worden geconfigureerd. Het kan worden geschreven naar de HTTP-cookieheader of naar een aangepaste SOAP-header. Met de aangepaste SOAP-headerbenadering kunt u dit protocol gebruiken met niet-HTTP-protocollen (bijvoorbeeld TCP of Named Pipes). Er zijn twee klassen, namelijk MessageHeaderContextManager en HttpCookieContextManager, die deze twee opties implementeren.

Beide schrijven de context-id naar het bericht op de juiste manier. De klasse schrijft deze bijvoorbeeld MessageHeaderContextManager naar een SOAP-header in de WriteContext methode.

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

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

  message.Headers.Add(contextHeader);
}

Zowel de als ReadContextId de ApplyContext methoden in de DurableInstanceContextChannelBase klasse roepen respectievelijk de IContextManager.ReadContext en IContextManager.WriteContext, aan. Deze contextbeheerders worden echter niet rechtstreeks door de DurableInstanceContextChannelBase klasse gemaakt. In plaats daarvan wordt de ContextManagerFactory klasse gebruikt om die taak uit te voeren.

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

De ApplyContext methode wordt aangeroepen door de verzendende kanalen. Hiermee wordt de context-id toegevoegd aan de uitgaande berichten. De ReadContextId methode wordt aangeroepen door de ontvangende kanalen. Deze methode zorgt ervoor dat de context-id beschikbaar is in de inkomende berichten en deze toevoegt aan de Properties verzameling van de Message klasse. Het genereert ook een CommunicationException in geval van een fout bij het lezen van de context-id en zorgt er dus voor dat het kanaal wordt afgebroken.

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

Voordat u doorgaat, is het belangrijk om inzicht te hebben in het gebruik van de Properties verzameling in de Message klasse. Properties Deze verzameling wordt meestal gebruikt bij het doorgeven van gegevens van lager naar de bovenste niveaus van de kanaallaag. Op deze manier kunnen de gewenste gegevens op een consistente manier aan de bovenste niveaus worden verstrekt, ongeacht de protocoldetails. Met andere woorden, de kanaallaag kan de context-id verzenden en ontvangen als een SOAP-header of een HTTP-cookieheader. Maar het is niet nodig dat de bovenste niveaus over deze details weten omdat de kanaallaag deze informatie beschikbaar maakt in de Properties verzameling.

Nu alle DurableInstanceContextChannelBase tien van de benodigde interfaces (IOutputChannel, IInputChannel, IOutputSessionChannel, IInputSessionChannel, IRequestChannel, IReplyChannel, IRequestSessionChannel, IReplySessionChannel, IDuplexChannel, IDuplexChannel, IDuplexSessionChannel) moeten worden geïmplementeerd. Ze lijken op elk beschikbaar berichtuitwisselingspatroon (datagram, simplex, duplex en hun sessievolle varianten). Elk van deze implementaties neemt de basisklasse over die eerder is beschreven en aanroepen ApplyContext en ReadContextId op de juiste wijze. Bijvoorbeeld, DurableInstanceContextOutputChannel waarmee de IOutputChannel-interface wordt geïmplementeerd, wordt de ApplyContext methode aangeroepen vanuit elke methode waarmee de berichten worden verzonden.

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

Aan de andere kant, DurableInstanceContextInputChannel waarmee de IInputChannel interface wordt geïmplementeerd, wordt de ReadContextId methode aangeroepen in elke methode, die de berichten ontvangt.

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

Afgezien hiervan delegeren deze kanaal-implementaties de methode-aanroepen naar het kanaal eronder in de kanaalstack. Sessionful varianten hebben echter een basislogica om ervoor te zorgen dat de context-id wordt verzonden en alleen-lezen is voor het eerste bericht dat ervoor zorgt dat de sessie wordt gemaakt.

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

Deze kanaal-implementaties worden vervolgens door de klasse en DurableInstanceContextBindingElementSection klasse aan de DurableInstanceContextBindingElement WCF-kanaalruntime toegevoegd. Zie de voorbeelddocumentatie van het HttpCookieSession-kanaal voor meer informatie over bindingselementen en secties met bindingselementen.

Extensies voor servicemodellagen

Nu de context-id door de kanaallaag is gereisd, kan het servicegedrag worden geïmplementeerd om de instantiëring aan te passen. In dit voorbeeld wordt een opslagbeheerder gebruikt om de status van of naar het permanente archief te laden en op te slaan. Zoals eerder uitgelegd, biedt dit voorbeeld een opslagbeheerder die GEBRUIKMAAKT van SQL Server 2005 als back-uparchief. Het is echter ook mogelijk om aangepaste opslagmechanismen toe te voegen aan deze extensie. Hiervoor wordt een openbare interface gedeclareerd, die door alle opslagmanagers moet worden geïmplementeerd.

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

De SqlServerStorageManager klasse bevat de standaard IStorageManager implementatie. In de SaveInstance methode wordt het opgegeven object geserialiseerd met behulp van de XmlSerializer en wordt het opgeslagen in de 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 de GetInstance methode worden de geserialiseerde gegevens gelezen voor een bepaalde context-id en wordt het object dat van deze gegevens is gemaakt, geretourneerd naar de aanroeper.

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

Gebruikers van deze opslagmanagers mogen ze niet rechtstreeks instantiëren. Ze gebruiken de StorageManagerFactory klasse, waarmee de details van het maken van opslagbeheer worden geabstraheerd. Deze klasse heeft één statisch lid, GetStorageManagerwaarmee een exemplaar van een bepaald type opslagbeheer wordt gemaakt. Als de typeparameter is null, maakt deze methode een exemplaar van de standaardklasse SqlServerStorageManager en retourneert deze. Ook wordt het opgegeven type gevalideerd om ervoor te zorgen dat de IStorageManager interface wordt geïmplementeerd.

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

De benodigde infrastructuur voor het lezen en schrijven van exemplaren uit de permanente opslag wordt geïmplementeerd. Nu moeten de benodigde stappen worden uitgevoerd om het servicegedrag te wijzigen.

Als eerste stap van dit proces moeten we de context-id opslaan, die via de kanaallaag naar de huidige InstanceContext is gekomen. InstanceContext is een runtime-onderdeel dat fungeert als de koppeling tussen de WCF-dispatcher en het service-exemplaar. Het kan worden gebruikt om aanvullende status en gedrag te bieden aan het service-exemplaar. Dit is essentieel omdat in sessievolle communicatie de context-id alleen met het eerste bericht wordt verzonden.

WCF maakt het mogelijk om het Runtime-onderdeel InstanceContext uit te breiden door een nieuwe status en gedrag toe te voegen met behulp van het uitbreidbare objectpatroon. Het uitbreidbare objectpatroon wordt in WCF gebruikt om bestaande runtimeklassen uit te breiden met nieuwe functionaliteit of om nieuwe statusfuncties toe te voegen aan een object. Er zijn drie interfaces in het uitbreidbare objectpatroon : IExtensibleObject<T>, IExtension<T> en IExtensionCollection<T>:

  • De IExtensibleObject<T-interface> wordt geïmplementeerd door objecten die uitbreidingen toestaan die hun functionaliteit aanpassen.

  • De IExtension<T-interface> wordt geïmplementeerd door objecten die uitbreidingen zijn van klassen van het type T.

  • De IExtensionCollection<T-interface> is een verzameling IExtensions waarmee IExtensions kunnen worden opgehaald op basis van hun type.

Daarom moet een InstanceContextExtension-klasse worden gemaakt die de IExtension-interface implementeert en de vereiste status definieert om de context-id op te slaan. Deze klasse biedt ook de status voor het opslaan van de opslagmanager die wordt gebruikt. Zodra de nieuwe status is opgeslagen, is het niet mogelijk om deze te wijzigen. Daarom wordt de status opgegeven en opgeslagen in het exemplaar op het moment dat deze wordt samengesteld en vervolgens alleen toegankelijk met alleen-lezeneigenschappen.

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

De klasse InstanceContextInitializer implementeert de interface IInstanceContextInitializer en voegt de extensie van de instantiecontext toe aan de extensions-verzameling van de InstanceContext die wordt samengesteld.

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

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

Zoals eerder beschreven, wordt de context-id gelezen uit de Properties verzameling van de Message klasse en doorgegeven aan de constructor van de extensieklasse. Dit laat zien hoe informatie op een consistente manier kan worden uitgewisseld tussen de lagen.

De volgende belangrijke stap is het overschrijven van het proces voor het maken van het service-exemplaar. MET WCF kunt u aangepast instantiatiegedrag implementeren en deze koppelen aan de runtime met behulp van de IInstanceProvider-interface. De nieuwe InstanceProvider klasse wordt geïmplementeerd om die taak uit te voeren. Het servicetype dat van de instantieprovider wordt verwacht, wordt geaccepteerd in de constructor. Later wordt dit gebruikt om nieuwe exemplaren te maken. In de GetInstance implementatie wordt een exemplaar van een opslagmanager gemaakt op zoek naar een persistent exemplaar. Als het retourneert null, wordt er een nieuw exemplaar van het servicetype geïnstantieerd en geretourneerd naar de beller.

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

De volgende belangrijke stap is het installeren van de InstanceContextExtension, InstanceContextInitializeren InstanceProvider klassen in de runtime van het servicemodel. Een aangepast kenmerk kan worden gebruikt om de service-implementatieklassen te markeren om het gedrag te installeren. De DurableInstanceContextAttribute bevat de implementatie voor dit kenmerk en implementeert de interface om de IServiceBehavior volledige serviceruntime uit te breiden.

Deze klasse heeft een eigenschap die het type opslagbeheer accepteert dat moet worden gebruikt. Op deze manier kunnen de gebruikers met de implementatie hun eigen IStorageManager implementatie opgeven als parameter van dit kenmerk.

In de ApplyDispatchBehavior implementatie wordt het InstanceContextMode huidige ServiceBehavior kenmerk gecontroleerd. Als deze eigenschap is ingesteld op Singleton, is het inschakelen van duurzame instancing niet mogelijk en wordt er een InvalidOperationException gegenereerd om de host op de hoogte te stellen.

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

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

Hierna worden de exemplaren van het opslagbeheer, initialisatie van instantiecontext en de exemplaarprovider gemaakt en geïnstalleerd in de DispatchRuntime gemaakte voor elk eindpunt.

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

In de samenvatting tot nu toe heeft dit voorbeeld een kanaal geproduceerd waarmee het aangepaste wire-protocol voor aangepaste context-id-uitwisseling is ingeschakeld en wordt ook het standaardgedrag voor instancing overschreven om de exemplaren uit de permanente opslag te laden.

Wat er nog is, is een manier om het service-exemplaar op te slaan in de permanente opslag. Zoals eerder besproken, is er al de vereiste functionaliteit om de status in een IStorageManager implementatie op te slaan. We moeten dit nu integreren met de WCF-runtime. Een ander kenmerk is vereist dat van toepassing is op de methoden in de service-implementatieklasse. Dit kenmerk moet worden toegepast op de methoden die de status van het service-exemplaar wijzigen.

De SaveStateAttribute klasse implementeert deze functionaliteit. Het implementeert IOperationBehavior ook klasse om de WCF-runtime voor elke bewerking te wijzigen. Wanneer een methode is gemarkeerd met dit kenmerk, roept de WCF-runtime de ApplyBehavior methode aan terwijl de juiste DispatchOperation wordt samengesteld. Er is één regel code in deze methode-implementatie:

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

Met deze instructie maakt u een exemplaar van het OperationInvoker type en wijst u deze toe aan de Invoker eigenschap van de DispatchOperation constructie. De OperationInvoker klasse is een wrapper voor de standaardbewerkingsinroeper die is gemaakt voor de DispatchOperation. Met deze klasse wordt de IOperationInvoker-interface geïmplementeerd. In de Invoke methode-implementatie wordt de werkelijke aanroep van de methode gedelegeerd aan de aanroeper van de interne bewerking. Voordat u echter de resultaten retourneert, wordt de opslagbeheerder in het InstanceContext opslagexemplaren gebruikt om het service-exemplaar op te slaan.

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;

De extensie gebruiken

Zowel de uitbreidingen van de kanaallaag als de servicemodellaag worden uitgevoerd en kunnen nu worden gebruikt in WCF-toepassingen. Services moeten het kanaal toevoegen aan de kanaalstack met behulp van een aangepaste binding en vervolgens de service-implementatieklassen markeren met de juiste kenmerken.

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

Clienttoepassingen moeten de DurableInstanceContextChannel toevoegen aan de kanaalstack met behulp van een aangepaste binding. Als u het kanaal declaratief wilt configureren in het configuratiebestand, moet de sectie bindingselement worden toegevoegd aan de verzameling bindingselementextensies.

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

Nu kan het bindingselement worden gebruikt met een aangepaste binding, net als andere standaardbindingselementen:

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

Conclusie

In dit voorbeeld ziet u hoe u een aangepast protocolkanaal maakt en hoe u het servicegedrag aanpast om dit in te schakelen.

De extensie kan verder worden verbeterd door gebruikers de IStorageManager implementatie te laten opgeven met behulp van een configuratiesectie. Hierdoor kunt u het backing-archief wijzigen zonder de servicecode opnieuw te compileren.

Daarnaast kunt u proberen een klasse te implementeren (bijvoorbeeld StateBag), waarmee de status van het exemplaar wordt ingekapseld. Deze klasse is verantwoordelijk voor het behouden van de status wanneer deze wordt gewijzigd. Op deze manier kunt u voorkomen dat u het SaveState kenmerk gebruikt en het persistente werk nauwkeuriger uitvoert (u kunt bijvoorbeeld de status behouden wanneer de status daadwerkelijk wordt gewijzigd in plaats van deze op te slaan telkens wanneer een methode met het SaveState kenmerk wordt aangeroepen).

Wanneer u het voorbeeld uitvoert, wordt de volgende uitvoer weergegeven. De klant voegt twee items toe aan zijn winkelwagen en haalt vervolgens de lijst met items in de winkelwagen van de service op. Druk in elk consolevenster op Enter om de service en client af te sluiten.

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

Notitie

Als u de service opnieuw bouwt, wordt het databasebestand overschreven. Als u de status wilt observeren die behouden blijft voor meerdere uitvoeringen van het voorbeeld, moet u het voorbeeld niet opnieuw opbouwen tussen uitvoeringen.

Het voorbeeld instellen, compileren en uitvoeren

  1. Zorg ervoor dat u de eenmalige installatieprocedure voor de Windows Communication Foundation-voorbeelden hebt uitgevoerd.

  2. Volg de instructies in Het bouwen van de Windows Communication Foundation-voorbeelden om de oplossing te bouwen.

  3. Als u het voorbeeld wilt uitvoeren in een configuratie met één of meerdere computers, volgt u de instructies in Het uitvoeren van de Windows Communication Foundation-voorbeelden.

Notitie

U moet SQL Server 2005 of SQL Express 2005 uitvoeren om dit voorbeeld uit te voeren. Als u SQL Server 2005 uitvoert, moet u de configuratie van de verbindingsreeks van de service wijzigen. Bij het uitvoeren van cross-machine is SQL Server alleen vereist op de servercomputer.