Maintaining State and Sequencing Operations in Windows Communication Foundation 4

  • 11/23/2010

Maintaining State by Using a Durable Service

In the earlier section, "Maintaining State with the PerCall Instance Context Mode" on page 262, you saw how to maintain state in a session-less service by providing your own code to serialize and deserialize state information. You were also made aware of some of the complications that you need to address if you wish to follow this approach in a professional application. These are non-trivial issues. Fortunately, WCF provides the notion of durable services and durable operations that can help you.

A durable service is a WCF service that uses sessions to maintain state information. However, you can suspend or even halt a session, releasing the resources associated with the service instance while preserving the state of the session. Later, you can start a new service instance, resume a session, and reload the state for that session into the service instance. Durable services are ideal for long-running sessions, during which a client application may be inactive for a period of time; the session and resources associated with an inactive client can be swapped out and reloaded when the client becomes active again. The workflow model for building scalable WCF services relies heavily on durable services (as you will see in Chapter 8), as does Windows Server AppFabric, if you are hosting WCF services in an enterprise environment. However, you can also use durable services outside of these scenarios. You will investigate how in the set of exercises to follow.

You indicate that a service is durable by tagging it with the DurableService attribute. A durable service requires a data store to persist the state of its sessions. WCF and Workflow Foundation provide the SQL Persistence Provider for storing session state in a SQL Server database. You can also build your own custom persistence provider if you wish to use some other mechanism. You configure a durable service to reference a persistence provider and provide the details for connecting to the persistence store used by that provider.

When a service instance is started and a new session is created, the WCF runtime generates a unique instance ID for that session. This instance ID is passed in the header of SOAP messages that flow between the client application and the service, and the WCF runtime uses this instance ID to correlate the client requests with the corresponding service instance. When the client closes the connection to the service, the WCF runtime saves the state of the session, including the instance ID, to the persistence store. Later on, if the client application resumes, it can populate the header of the request messages that it sends to the WCF service host with the instance ID of the earlier session. The WCF runtime for the service can create a new session, look up the session state from the persistence store, and use this information to populate the session. To the client application, this new session appears to be exactly the same as the old one.

As well as marking a service as durable, you also need to specify which operations can cause the service to save and resume session state. You do this with the DurableOperation attribute. This attribute provides similar functionality to the IsInitiating and IsTerminating properties of the OperationBehavior attribute; you can set a property named CanCreateInstance to true to specify that the WCF runtime can create a new instance of a durable service when the operation is invoked. You can also set this property to false to indicate that the operation can only run by using an existing instance. Additionally, you can set the CompletesInstance property to true to indicate that the operation finishes the session, and any saved state held in the persistence store should be removed when the operation completes.

In the following exercises, you will modify the ShoppingCartService to be a durable service, and examine how to interact with this service from a simple graphical client application.

Examine the ShoppingCartGUIClient Application

  1. In Visual Studio, open the ShoppingCart solution located in the DurableShoppingCart folder in the Microsoft Press\WCF Step By Step\Chapter 7 folder.

    This solution contains a version of the ShoppingCartService but with the OperationBehavior attributes removed. The service implements the PerSession instance context mode. The ShoppingCartGUIClient project is a WPF application that invokes the operations in the ShoppingCartService service and displays the results in a GUI.

  2. In the ShoppingCartGUIClient project, open the MainWindow.xaml file in the Design View window.

    When the user runs the application, she can enter a product number and then click AddItem to add the specified item to the shopping cart. The contents of the shopping cart are displayed in the text area below the buttons. The user can remove an item from the shopping cart by clicking the Remove Item button, and the contents of the shopping cart will be redisplayed. The user can click the Checkout button to invoke the Checkout operation, which also clears the shopping cart.

  3. Start the solution without debugging. In the Shopping Cart GUI Client window, enter WB-H098 in the Product Number text box, and then click Add Item. After a short delay, the contents of the shopping cart will appear, displaying a water bottle, as shown in the following image:

    httpatomoreillycomsourcemspimages1724008.png
  4. Click Add Item again and verify that the volume and total cost are updated.

  5. In the Product Number text box, type SA-M198, and then click Add Item one more time. A mountain seat assembly should be added and appear in the shopping cart displayed in the window.

  6. Click Remove Item. The mountain seat assembly should be removed from the shopping cart.

  7. Click Checkout. The shopping cart should be emptied.

  8. Close the Shopping Cart GUI Client window. In the service host application console window, press Enter to stop the service.

  9. In Solution Explorer, expand the MainWindow.xaml node in the ShoppingCartGUI Client project, and then open the MainWindow.xaml.cs file in the Code And Text Editor window.

    This file contains the code behind the user interface. The highlights are reproduced in the following code example:

    public partial class MainWindow : Window
    {
        private ShoppingCartServiceClient proxy = null;
        ...
        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            // Connect to the ShoppingCartService service
            proxy = new ShoppingCartServiceClient(
                "WS2007HttpBinding_IShoppingCartService");
        }
    
        private void Window_Unloaded(object sender, RoutedEventArgs e)
        {
            // Disconnect from the service
            proxy.Close();
        }
    
        // Add the item specified in the productNumber Text Box to the shopping cart
        private void addItem_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                // Add the item to the shoppping cart
                proxy.AddItemToCart(productNumber.Text);
    
                // Display the shopping cart
                string cartContents = proxy.GetShoppingCart();
                shoppingCartContents.Text = cartContents;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Error adding item to cart",
                    MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }
    
        // Remove the item specified in the productNumber Text Box from the shopping cart
        private void removeItem_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                // Remove the item from the shoppping cart
                proxy.RemoveItemFromCart(productNumber.Text);
    
                // Display the shopping cart
                string cartContents = proxy.GetShoppingCart();
                shoppingCartContents.Text = cartContents;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Error removing item from cart",
                    MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }
    
        // Checkout
        private void checkout_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                proxy.Checkout();
    
                // Clear the shopping cart displayed in the window
                shoppingCartContents.Clear();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Error checking out",
                    MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }
    }

    The connection to the service is held in the proxy variable. The connection is established when the window appears, and it is terminated when the window disappears.

    The addItem_Click method runs when the user clicks Add Item. This method invokes the AddItemToCart operation, passing the product number specified in the productNumber text box as the parameter. The method then calls GetShoppingCart to retrieve the contents of the shopping cart from the service, which it then displays in the shoppingCartContents text area in the window.

    The logic behind the removeItem_Click method is similar. This method runs when the user clicks Remove Item, and it calls the RemoveItemFromCart operation followed by GetShoppingCart to retrieve the updated shopping cart information.

    The checkout_Click method runs when the user clicks Checkout. This method calls the Checkout operation and then clears the shopping cart displayed in the window.

This client application is straightforward, but it exhibits some very poor practice that does not lend itself to building a professional, scalable system. The problem is that the connection to the service is established and the session initiated when the user starts the application and opens the window, and this connection and session remain in memory in the service host until the user closes the window and stops the application. If the user forgets to close the client application before she goes off on holiday for two weeks, the resources associated with the session will remain in memory for this time. Additionally, if the service host is shut down during this period, the session state information (the shopping cart) will be lost.

Reconfiguring ShoppingCartService as a durable service and making some adjustments to the code in the client application can rectify these problems. You will start by creating the persistence store for the durable service.

Create the Persistence Store for the SQL Persistence Provider

  1. In Visual Studio, select View | Server Explorer.

  2. In Server Explorer, right-click Data Connections, and then click Create New SQL Server Database. In the Create New SQL Server Database dialog box, set the Server Name to .\SQLExpress, click Use Windows Authentication, enter WCFPersistence in the New Database Name text box, and then click OK.

    httpatomoreillycomsourcemspimages1724010.png
  3. From the File menu, click Open, and then click File. In the Open File dialog box move to the folder C:\Windows\Microsoft.NET\Framework\v4.0.30319\SQL\en, select the SqlPersistenceProviderSchema.sql file, and then click Open.

    This SQL script creates the InstanceData table in the persistence database. The SQL Persistence Provider uses this table to store session information.

  4. From the Data menu, click Transact-SQL Editor, click Connection, and then click Connect. In the Connect to Database Engine dialog box, in the Server Name text box, enter .\SQLExpress. Ensure that Authentication is set to Windows Authentication, and then click Connect.

  5. In the Visual Studio toolbar, in the Database drop-down, click WCFPersistence.

  6. From the Data menu, click Transact-SQL Editor, and then click Execute SQL. Verify that the SQL script runs and displays the message “Command(s) completed successfully.”

  7. Follow the process described in steps 3 through 6 to open and run the SqlPersistence ProviderLogic.sql script located in the C:\Windows\Microsoft.NET\Framework\v4.0.30319\SQL\en folder.

    This script creates the stored procedures required by the SQL Persistence Provider to insert, update, and delete information about sessions in the InstanceData table. These operations are performed in a thread-safe manner by using logic that locks the session data while it is being manipulated.

You can now modify the service and configure it as a durable service that uses this persistence store.

Reconfigure the ShoppingCartService Service as a Durable Service

  1. In Solution Explorer, click the ShoppingCartService project. From the Project menu, select ShoppingCartService Properties.

  2. In the ShoppingCartService Properties window, click the Application tab. In the Target Framework drop-down, click .NET Framework 4. In the Target Framework Change message box, click Yes to allow Visual Studio to change the target framework.

    The project was originally built by using the more lightweight version of the .NET Framework provided by the Client Profile. However, the persistence functionality of durable services requires assemblies that are only available with the full-blown version of the .NET Framework.

  3. Add a reference to the System.WorkflowServices assembly to the ShoppingCartService project.

    This assembly contains the implementation of the DurableService and DurableOperation attributes.

  4. Open the IShoppingCartService.cs file for the ShoppingCartService project in the Code And Text Editor window. Examine the ShoppingCartItem class.

    Remember that this class holds the session data for each service instance. You marked this class as serializable earlier so you could save instances of this class as XML data. The SQL Persistence Provider also requires that this—and any other data stored as part of the session state—is serializable, so leave the Serializable attribute in place.

  5. Open the ShoppingCartService.cs file for the ShoppingCartService project in the Code And Text Editor window. Add the following using statement to the list at the top of the file:

    using System.ServiceModel.Description;

    The DurableService and DurableOperation attributes implemented by the System.WorkflowServices assembly are defined in this namespace.

  6. Verify that the InstanceContextMode property of the ServiceBehavior attribute for the ShoppingCartServceImpl class is set to InstanceContextMode.PerSession.

    All durable services must implement sessions.

  7. Add the Serializable and DurableService attributes (shown in bold in the following) to the ShoppingCartServiceImpl class.

    [ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)]
    [Serializable]
    [DurableService]
    public class ShoppingCartServiceImpl : IShoppingCartService
    {
        ...
    }

    The SQL Persistence Provider requires that all durable services are also serializable so that their state can be saved to the persistence database.

  8. Locate the AddItemToCart method in the ShoppingCartServiceImpl class. Tag this method as a durable operation and set the CanCreateInstance property to true, as shown in the following.

    [DurableOperation(CanCreateInstance = true)]
    public bool AddItemToCart(string productNumber)
    {
        ...
    }

    Following the same logic described in the section "Sequencing Operations in a WCF Service" on page 271, a client application can use this operation to start a new session if one has not already been created.

  9. Mark the RemoveItemFromCart method as a durable operation, but set the CanCreateInstance property to false. Do the same for the GetShoppingCart method.

    [DurableOperation(CanCreateInstance = false)]
    public bool RemoveItemFromCart(string productNumber)
    {
        ...
    }
    ...
    [DurableOperation(CanCreateInstance = false)]
    public string GetShoppingCartCart()
    {
        ...
    }

    Neither of these methods can be invoked unless a session already exists.

  10. Tag the Checkout operation with the DurableOperation attribute. Set the CanCreateInstance property to false and set the CompletesInstance property to true.

    [DurableOperation(CanCreateInstance = false, CompletesInstance = true)]
    public bool Checkout()
    {
        ...
    }

    This operation finishes a session and removes the state from the persistence store when it completes.

  11. In Solution Explorer, click the ShoppingCartHost project. From the Project menu, select ShoppingCartHost Properties. In the ShoppingCartHost Properties window, click the Application tab. In the Target Framework drop-down, click .NET Framework 4. In the Target Framework Change message box, click Yes to allow Visual Studio to change the target framework.

    The ShoppingCartHost project references the ShoppingCartService project and must run by using the same version of the .NET Framework.

  12. Open the App.config file for the ShoppingCartHost project in the Code And Text Editor window. Add the following connection string (shown in bold) to the connectionStrings section.

    <?xml version="1.0"?>
    <configuration>
      <connectionStrings>
        <add name="AdventureWorksEntities" connectionString="..."/>
        <add name="DurableServiceConnectionString" connectionString=
    "Data Source=.\SQLExpress;Initial Catalog=WCFPersistence;Integrated Security=True"/>
      </connectionStrings>
      ...
    </configuration>

    This connection string contains the information that the SQL Persistence Provider requires for connecting to the SQL Server database that you created in the previous exercise. You will reference this connection by its name, DurableServiceConnectionString, in the WCF configuration for the service host.

  13. Save the App.config file, and then edit it again using the Service Configuration Editor.

  14. In the Configuration pane, expand the Advanced folder, and then click Service Behaviors. In the Service Behaviors pane, click New Service Behavior Configuration.

  15. In the Behavior: NewBehavior() pane, in the Name field, enter DurableServiceBehavior. In the lower pane, click Add.

  16. In the Adding Behavior Element Extension Sections dialog box, click persistenceProvider, and then click Add.

  17. In the Configuration pane, under the DurableServiceBehavior node, click the persistenceProvider node.

  18. In the PersistenceProvider pane, in the Type field, enter System.ServiceModel. Persistence.SqlPersistenceProviderFactory.

  19. In the Configuration pane, expand the persistenceProvider node, and then click persistence

    ProvideArguments.

  20. In the PersistenceProviderArguments pane, click New.

  21. In the Persistence Provider Arguments Editor dialog box, in the Name field, enter connectionStringName. In the Value field enter DurableServiceConnectionString, and then click OK.

    These parameters specify that the WCF runtime should use the SqlPersistenceProviderFactory class to create a SqlPersistenceProvider object for persisting session data. This object will use the information specified by the connectionStringName parameter to connect to the SQL Server database.

  22. In the Configuration pane, under the Services folder, click the ShoppingCartService.ShoppingCartServiceImpl service. In the Service:ShoppingCartService.ShoppingCartServiceImpl pane, set the BehaviorConfiguration property to DurableServiceBehavior.

  23. In the Configuration pane, expand the ShoppingCartService.ShoppingCartServiceImpl service, expand the Endpoints folder, and then click the (Empty Name) endpoint.

  24. In the Service Endpoint pane, change the Binding property to wsHttpContextBinding.

    As mentioned earlier, the instance ID generated by the durable service is passed in the header of the SOAP messages that are exchanged by the client application and the service. The protocol used by the service must populate and examine this information automatically; the wsHttpContextBinding binding provides this functionality.

  25. Save the configuration file, and then exit the Service Configuration Editor.

The next step is to modify the client application. You must configure it to use the same binding as the service (wsHttpContextBinding), and you will also update it to take better advantage of the durable service. Specifically, you will modify the code behind the MainWindow window to open and close connections to the service as they are required rather than creating a connection when the application starts and holding this connection open until the application finishes.

Update the ShoppingCartGUIClient Application

  1. Open the app.config file for the ShoppingCartGUIClient project in the Code And Text Editor window. Modify the client endpoint to use the wsHttpContextBinding binding, and change the name of the endpoint to WSHttpContextBinding_IShoppingCart Service, for consistency, as shown in bold in the following:

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <system.serviceModel>
        <client>
          <endpoint address="http://localhost:9000/ShoppingCartService/ShoppingCart
              Service.svc"
              binding="wsHttpContextBinding" bindingConfiguration=""
              contract="ShoppingCartClient.ShoppingCartService.ShoppingCartService"
              name="WSHttpContextBinding_IShoppingCartService" kind=""
              endpointConfiguration="">
          </endpoint>
        </client>
      </system.serviceModel>
    </configuration>
  2. Open the MainWindow.xaml.cs file in the Code And Text Editor window. Comment out the code in the Window_Loaded and Window_Unloaded methods.

  3. Add the following using statements to the list at the top of the file:

    using System.ServiceModel;
    using System.ServiceModel.Channels;
  4. In the MainWIndow class, add the private context variable (shown in bold in the following) after the definition of the proxy variable:

    public partial class MainWindow : Window
    {
        private ShoppingCartServiceClient proxy = null;
        private IDictionary<string, string> context = null;
        ...
    }

    You will use the context variable to capture the context generated by the service when it creates the session on the initial call to the AddItemToCart operation. The context is a dictionary of key/value pairs and will include the instance ID (with the key “instanceId”) that the WCF runtime creates and uses to identify the session and correlate with the client application. You will pass this context in the SOAP header of subsequent calls to operations.

  5. Modify the code in the try block of the addItem_Click method, as shown in bold in the following:

    // Add the item specified in the productNumber Text Box to the shopping cart
    private void addItem_Click(object sender, RoutedEventArgs e)
    {
        try
        {
           // Create the proxy and connect to the service
           using (proxy = new ShoppingCartServiceClient(
               "WSHttpContextBinding_IShoppingCartService"))
           {
               // If the context is not null, then the client application
               // has already created the durable session.
               // Set the context in the IContextManager object for
               // the proxy so that this context is passed in the SOAP
               // header of the AddItemToCart and
               // GetShoppingCart requests.
               IContextManager contextManager =
                   proxy.InnerChannel.GetProperty<IContextManager>();
               if (context != null)
               {
                  contextManager.SetContext(context);
               }
    
                // Add the item to the shoppping cart
                proxy.AddItemToCart(productNumber.Text);
    
                // If the context is null, then this was the first call
                // made to the session.
                // Capture the context and save it so it can be
                // passed to subsequent requests
                if (context == null)
                {
                    context = contextManager.GetContext();
                    MessageBox.Show(context["instanceId"], "New context created",
                                    MessageBoxButton.OK, MessageBoxImage.Information);
                }
    
                // Display the shopping cart
                string cartContents = proxy.GetShoppingCart();
                shoppingCartContents.Text = cartContents;
            }
        }
        catch (Exception e)
        {
            ...
        }
    }

    The main items to notice in this code are:

    • The method now creates the proxy object which it uses to communicate with the service. The method also destroys the proxy object when it has finished with it; this action closes the communications channel with the service and releases the in-memory resources associated with the session. At this point the WCF runtime for the service persists the session state to the persistence store.

    • The name of the endpoint used to create the proxy is WSHttpContextBinding_IShoppingCartService; this is the name that you specified in the application configuration file.

    • The method creates an IContextManager object which provides access to the context information held in the SOAP header of messages sent and received by using the proxy. Notice that you obtain a reference to this object by using the GetProperty method of the InnerChannel property of the proxy. The InnerChannel property gives you direct access to the communications channel used by the proxy.

    • The method examines the context variable, and if this variable is not null, then the user must have clicked the Add Item button previously to initiate the session and create the shopping cart. In this case, before calling AddItemToCart again, the code invokes the SetContext method of the IContextManager object with the previously saved context information. The IContextManager object will pass this information in the SOAP header as part of the subsequent AddItemToCart and GetShoppingCart request messages, and the WCF runtime for the service will use this information to resurrect the session state associated with this context.

    • The code that retrieves the context from the SOAP header of the response received from the service after calling the AddItemToCart operation and saves it if the context variable is null. In this case, this is the first time that the user has clicked the Add Item button, so the WCF runtime has created an entirely new session. The context is passed back to the client application in the SOAP header of the response message, and the client application can access it through the GetContext method of the IContextManager object. For your information, the code also displays the instance ID found in the context. You will see that the instance ID is simply a GUID that the SQL Persistence Provider uses as the key for storing and retrieving information in the persistence database.

  6. Make the changes highlighted in bold in the code that follows to the removeItem_Click method. Remember that this method calls the RemoveItemFromCart operation which cannot be used to initiate a session. The logic in this method therefore assumes that the session has already been created, and so it simply populates the IContextManager object with the previously saved context information. This context is transmitted to the service in the SOAP header as part of the RemoveItemFromCart and GetShoppingCart request messages.

    // Remove the item specified in the productNumber Text Box from the shopping cart
    private void removeItem_Click(object sender, RoutedEventArgs e)
    {
        try
        {
            // Create the proxy and connect to the service
            using (proxy =
                new ShoppingCartServiceClient("WSHttpContextBinding_IShoppingCartService"))
            {
                // Set the context in the IContextManager object for the proxy so
                // that this context s passed in the SOAP header of the
                // RemoveItemFromCart and GetShoppingCart requests.
                IContextManager contextManager =
                    proxy.InnerChannel.GetProperty<IContextManager>();
                contextManager.SetContext(context);
    
                // Remove the item from the shoppping cart
                proxy.RemoveItemFromCart(productNumber.Text);
    
                // Display the shopping cart
                string cartContents = proxy.GetShoppingCart();
                shoppingCartContents.Text = cartContents;
            }
        }
        catch (Exception e)
        {
            ...
        }
    }
  7. Update the checkout_Click method, as shown in bold in the code that follows. The logic is similar to that in the removeItem_Click method. The principal difference is that the method sets the context variable to null as it finishes. This is because the Checkout operation terminates the session, so the context is no longer valid; a subsequent call to the AddItemToCart method must create a new session with a new context.

    // Checkout
    private void checkout_Click(object sender, RoutedEventArgs e)
    {
        try
        {
            // Create the proxy and connect to the service
            using (proxy = new ShoppingCartServiceClient(
                "WSHttpContextBinding_IShoppingCartService"))
            {
                // Set the context in the IContextManager object for the proxy
                // so that this context is passed in the SOAP header of the
                // Checkout request.
                IContextManager contextManager =
                    proxy.InnerChannel.GetProperty<IContextManager>();
                contextManager.SetContext(context);
                proxy.Checkout();
    
                // Clear the shopping cart displayed in the window
                shoppingCartContents.Clear();
    
                // Clear the context - the session has completed
                context = null;
            }
        }
        catch (Exception e)
        {
            ...
        }
    }

Test the Durable Service

  1. Start the solution without debugging. In the Shopping Cart GUI Client window, enter WB-H098 in the Product Number text box, and then click Add Item.

    A message box similar to that shown in the following image should appear, displaying the instance ID of the session that has been created by the durable service (the GUID for your session will be different from that shown in the image):

    httpatomoreillycomsourcemspimages1724012.png
  2. Click OK and verify that a water bottle has been added to the shopping cart.

  3. Leave the Shopping Cart GUI Client window and the service host console application window running and return to Visual Studio.

  4. In the Server Explorer pane, expand the Data Connections folder, expand the YourComputer\sqlexpress.WCFPersistence.dbo connection (where YourComputer is the name of your computer), expand Tables, right-click the InstanceData table, and then click Show Table Data.

    The SQL Persistence Provider stores session information in this table. You should see a single row in this table, and the value in the id column should be the same as the instance ID displayed previously by the message box in the client application. The session data is held in a binary format in the instance column.

  5. Return to the Shopping Cart GUI Client window. Enter SA-M198 in the Product Number text box, and then click Add Item.

    This time, no message box appears because the addItem_Click ascertains that the context variable has a value, so a session must have already been created. The context is passed to the ShoppingCartService service. The WCF runtime for the service creates a new service instance, extracts the instance ID from the context, retrieves the session data for this instance ID from the InstanceData table in the WCFPersistence database, and uses this data to populate the service instance. The mountain seat assembly is added to the shopping cart in this service instance. When the addItem_Click method finishes, the WCF runtime for the service saves the session data back to the InstanceData table before destroying the service instance.

  6. Leave the Shopping Cart GUI Client window running, but close the service host console application window. In Solution Explorer, right-click the ShoppingCartHost project, point to Debug, and then click Start New Instance.

    This step simulates a user leaving the client application running while the service host is shut down and restarted.

  7. Return to the Shopping Cart GUI Client window. Enter PU-M044 in the Product Number text box, and then click Add Item.

    A mountain bike pump is added to the shopping cart. Notice that the WCF runtime has successfully restored the existing contents of the shopping cart, despite the fact that the service host has been shut down and restarted in the period since the previous request.

  8. Click Checkout.

    This operation completes the session, and the shopping cart is emptied.

  9. Leave the Shopping Cart GUI Client window and the service host console application window running and return to Visual Studio displaying the data from the InstanceData table. In the Visual Studio toolbar, click the Execute SQL button (it has an image of a red exclamation mark). The table should now be empty.

    When the Checkout operation completed, the session was terminated, and the session information saved in the persistence store was removed.

  10. Close the Shopping Cart GUI Client window and the service host console application window.