Managing State


We mentioned the EnableSession attribute, which can be applied to the WebMethod attribute to enable session handling, and we showed how it allows access to the Application and Session objects via the System.Web.Services namespace. However, session handling with Web services is not nearly as straightforward as just using the Session and Application objects and hoping for the best. It merits a larger discussion in its own right.

One potential conundrum when you create a Web application is how to maintain state while using the inherently stateless protocol HTTP. Of course, with Web services this generally shouldn’t be a concern because normally you won’t need to keep track of state, but sometimes even Web services require a method for remembering state. For example, your Web service might need to make multiple connections to a client. Or, when you’re using transactions, your Web service might need to remember, for a transaction, the state of each process that involves the service.

Throughout the brief history of Web applications, the Web developer has had three choices in this matter: maintaining state on the server, maintaining state on the client, or abandoning the information completely when a connection is closed or cut off. The tradeoff is between making the code simpler (by maintaining state on the server or client) and making the code perform optimally (not keeping track of state at all).

You keep track of state in a Web service by exposing the ASP.NET Application and Session objects. In fact, the methods for accessing these objects are very straightforward.

You can expose the Session and Application objects in two ways:

  • By inheriting from the WebService class contained in the System.Web.Services namespace.

  • By accessing the HttpContext object via the HttpContext class’s Current property. The HttpContext object allows you to access all “classic” ASP objects.

Before we rush into looking at how to do this, let’s consider the circumstances under which you should keep track of state.

When Should You Maintain State?

Maintaining state isn’t always necessary when you use Web services, and indeed the additional overhead needed to maintain state can often greatly delay and damage a service, so think carefully before you implement state management. If at all possible, your Web service should be stateless.

If you consider the following example, you’ll see why it is often preferable to forget about state. To keep track of state, you could update the property of a particular object and access it whenever you need to, but this isn’t at all straightforward. For starters, Web methods support only methods, not property procedures, so the following code would be illegal:

public class Artist: System.Web.Services.WebService {     [WebMethod]     public string Biography     {         get         {             return Biography;         }         set         {                 Biography = "A short history";         }     } }

Any proxy class generated would not contain the necessary properties. Instead, you must enable the Session property of each property accessor individually, setting the EnableSession property to true as follows. (Again, this code is illegal; we’re using it just for theoretical purposes.)

public class Artist:System.Web.Services.WebService {     public string Biography;     {         [WebMethod(EnableSession = true)]         get         {             return (string) Session["Biography"];         }         [WebMethod(EnableSession = true)]         set         {                 Session["Biography"] = "A short history";         }     } }

We created a Session variable, Biography, in which we stored our value. To return a value from it, we explicitly cast the Session variable as a string. However, even if this solution were to work, it would undermine the advantages provided by Web services. If you want to get or set the property value, you must make a separate method call over the Internet for each call, and this will affect your Web service’s performance. Also, using Session information like this is artificial. Artist.get_Biography isn’t tied to Session["Biography"] by any other way than the Web method, so when the Web method is finished, the Session variable persists until it times out or the session is concluded. Also, because session variables are related to separate cookies with their own session IDs, in some situations you might need to use two separate Session IDs, such as for banking transactions, and this can cause further performance problems.

Rather than list the situations in which you shouldn’t keep track of state, it’s probably easier to list the times when you might use session management within a Web service:

  • When your Web service requires information from a data source (as in our example application)

  • When your Web service goes through a series of steps in a transaction that might have to be rolled back, depending on whether a step fails or succeeds

Even in these situations, good alternatives are available to using state management, as we will briefly touch on.

Maintaining State on the Server

You can store state on the server in three ways: by tracking state on a single server, by using an ASP.NET state service, or by using a SQL Server database.

In-Memory State

Classic ASP maintains state on a single server by using cookies on the client, which can then be referenced via the Session object. This practice is the fastest way of maintaining state but also the most liable to breakage. If the ASP/ASP.NET process gets stopped, all associated information is lost. The information can also be lost if a request to one server is sent to a different one instead.

Out-of-Process State

You can use two out-of-process state services with ASP.NET to keep track of state. The advantage of this approach is that it can be used over several servers, and state isn’t as vulnerable to being lost if the ASP/ASP.NET process is killed. The main problem with keeping track of out-of-state session processes is that using either service is much slower than maintaining in-memory state. The two types of out-of-process state are

  • StateServer An ASP.NET state service can be tracked via the Microsoft Management Console (MMC).

  • SQLServer SQLServer uses stored procedures, which must first be installed via the SQL Server Manager.

You typically use these forms of state management only when you’re using a Web farm configuration together with several servers. In fact, in-process session state is the only form of state management currently available in the .NET Framework. So we’ll concentrate on using state in Web services via ASP.NET.

Integrating ASP.NET’s Built-in State Facility into Web Services

One great downside of using sessions with classic ASP was that underneath all the wrapping it still relied on cookies to do its work. A user who turned off cookies would lose any benefit of sessions—and likely the entire functionality of the Web application as well. ASP.NET allows you to send cookieless information by munging the session state information to a URL that allows the user to reconnect to the session information.

For you to use session state, a SessionStateModule object must be present in the application. (It is by default.) You can then alter information about the session using the sessionState element in the machine.config file on the server.

The sessionState element has six attributes:

  • mode You can set this attribute to Off, InProc, StateServer, or SQLServer. It specifies where the session state information is to be held. These are basically the scenarios we have discussed; either session state handling is turned off or the information can be stored in process via the StateServer service or in a SQL database.

  • cookieless This attribute specifies whether to use “classic ASP” session cookies (false is the default setting) or whether to use the new ASP.NET cookieless munging procedure.

  • timeout Indicates how long, in minutes, the session can be idle before it is terminated.

  • stateConnectionString Specifies the location and port of the session state server.

  • sqlConnectionString Specifies the location of the SQL Server.

  • stateNetworkTimeout Specifies the number of seconds the TCP/IP network connection between the Web server and the state server can be idle before the session is abandoned.

You alter these settings for the whole application via the configuration file and then you can enable a session for each Web method by using the EnableSession attribute and setting it to true.

Two types of state can be enabled for a session. We’ll look at them next.

Application State

The Application object allows you to maintain application state by letting you store session information for the whole application. The Application object doesn’t have to be explicitly enabled; it is already available via the Context object, so any class deriving from the WebService class will automatically have access to the Application object.

The best way to demonstrate this is to build a simple example Web method. This Web method contains two pieces of information: an artist and a specific title the artist has performed. The Web service will store the name of the artist and then add the title to that artist information.

The code that does this task is broken down into two classes. The first class has two Web methods. The first Web method that is exposed stores the Artist’s Name in the Application object, under the variable “Artist”. The second method allows the user to retrieve the artist’s name.

public class SetArtist:System.Web.Services.WebService  {     [WebMethod]     public string SetArtistName(string Artist)     {         Application.Add("Artist", ArtistName);         return ((string) Application["Artist"]);     }     [WebMethod]     public string GetArtistName()     {         return((string) Application["Artist"]);     } }

This Web service uses the inherited Web service class to provide access to the Application object. You can just as easily use the second method of accessing the Application object’s state via the HttpContext object. We can write a second class, SetArtist class, that concatenates the title to the artist’s name in the Application object, by using the HttpContext object as follows:

public class SetTitle:System.Web.Services.WebService  {     [WebMethod]     public string AddTitle(string Title)     {         HttpContext ConObj = HttpContext.Current;         ConObj.Application["Artist"] =              (string) ConObj.Application["Artist"] + Title;         return ((string) ConObj.Application["Artist"]);     } }

We use the Current property of the HttpContext object to return a reference to the Application object.

Session State

The main difference between using session state instead of application state is that we have to explicitly enable the Session object by setting the EnableSession property of the Web method to True. Then all we need to do is substitute the occurrence of the Application object for the Session object in our first class:

public class SetArtist:System.Web.Services.WebService  {     [WebMethod(EnableSession = True)]     public string SetArtistName(string Artist)     {         Session.Add("Artist", ArtistName);         return ((string) Session["Artist"]);     }     [WebMethod(EnableSession = True)]     public string GetArtistName()     {         return((string) Session["Artist"]);     } }

and in our second class:

public class SetTitle:System.Web.Services.WebService  {     [WebMethod(EnableSession = True)]     public string AddTitle(string Title)     {         HttpContext ConObj = HttpContext.Current;         ConObj.Session["Artist"] =              (string) ConObj.Session["Artist"] + Title;         return ((string) ConObj.Session["Artist"]);     } }

As mentioned earlier, the Session object doesn’t offer an ideal solution because each time the Web service is called, a new session is invoked.

Custom Cookies

The final way you can use ASP.NET to monitor state information on the client is to store it in a cookie and submit it along with each request. If you are certain that your clients will have cookies enabled, you can use this method.

You can create a cookie and insert the relevant information into it and send it to the client, by using the Response object. Then, in a future Web method call, you can use the Request object to retrieve it. Both the Request and Response objects can be accessed via the Context object.

The code to store this session information is as follows:

public class SetArtist:System.Web.Services.WebService  {     [WebMethod]     public string SetArtistName(string Artist)     {         HttpCookie CustomCookie = new HttpCookie("artist");         CustomCookie.Values.Add("Artist", "The Monkees");         Context.Response.AppendCookie(CustomCookie)     }

The code to retrieve it looks like this:

    [WebMethod]     public string GetArtistName()     {         HttpCookie CustomCookie = Context.Response.Cookies("artist");         if(cookie.Values["artist"] == "The Monkees")         {             string message = "I'm a believer";             }         return message;     }

In this way, you can handle session state transparently and easily on the client. This can be a good solution if the information being stored doesn’t have to be secure, is only textual information, and is restricted to using HTTP.

Asynchronous Web Services

Web services also provide an ideal model for the remote calling of methods just as if they were present on the client’s machine. By default, calls to a Web service made from ASP.NET clients or from Windows Forms applications are synchronous and rely on immediate responses. However, even over the shortest distances some lag is likely, so in some situations it’s far better to get your client to poll the Web service and then come back later to pick up the response. The .NET Framework provides good support for this kind of asynchronous operation.

Some situations are much better suited to asynchronous operations than others. Any kind of operation in which the Web service might be blocked by a thread that must wait on specific information before continuing is not suited to asynchronous services. However, when you call a Web service that might take a long time to return a result, but the information isn’t critical to the continuation of the thread—as is likely in most situations in which a Web service is called over the Internet—asynchronous calls are the preferable solution. With an asynchronous call, your application can continue executing a thread after having issued a call for the Web service and won’t be held up by the lack of response. Situations in which this is useful are those involving a transaction or those that require the use of a user interface, where the lack of response might freeze the interface.

This model of asynchronous consumption might seem to have no bearing on the Web method itself. Whether a service is called asynchronously doesn’t directly affect the Web method because the call happens via the client and proxy class (as you shall see in the next chapter). However, there are some concerns relating to asynchronous behavior when you need your Web service to return information about work in progress, such as the status of a download or installation. In theory, your method will run from start to finish and not return any information until it has finished the job at hand.

The OneWay Property

The simplest trick for writing an asynchronous Web method is to use the OneWay property, which is part of the SoapRpcMethod attribute. Setting this property to true enables the client to return immediately because it won’t be waiting for a response value. In other words, the client doesn’t have to wait for the Web service to finish processing.

The Web service does this by returning a 202 HTTP status code, which indicates to the client that the service has started and that the client doesn’t need to come back and check that the service has finished. Instead, the Web service usually writes a value to a database, which can be picked up at a later time. OneWay methods aren’t allowed to have a return value for this obvious reason.

One possible application of the OneWay method is on a random-number generator. A Web service that has to generate thousands of sequences of unique random numbers (for lotto cards, for example) might take hours to complete, so you wouldn’t want the Web service’s client hanging around this long for an answer.

To set up this attribute, you can do the following:

public class GenerateRandomNumber: WebService  {     [SoapRpcMethod(OneWay=true)]     [WebMethod]     public void Generate()     {         //Logic for generating random number sequences     }      

As with most simple solutions, this has flaws. For example, you cannot uniquely identify which service kick-started the process. You might be able to identify a particular user, but what happens if that user makes several calls to the Web service? There is no way around this, so a better solution is needed.

Dividing the Labor

Another way to implement asynchronous services is to write several Web methods and get each Web method to perform a different action within the Web service. The task has to be started by one Web method and finished by another, but then you can get around the problem of not being able to return a value because one Web method can call another, and then a second can return a value.

Threading

The big issue when you use multiple Web methods is guaranteeing a secure and safe way of accessing the information. If you consider that several Web methods will be running every time the service is called and that the service can be called many times, you need some sort of locking procedure to guarantee a particular Web method’s exclusive access to a resource.

WS-Routing

WS-Routing is a SOAP-based protocol that allows the asynchronous routing of SOAP messages. It adds information to the SOAP header that describes the path from initial sender to ultimate receiver via intermediaries.

The proposal for WS-Routing was launched in late 2001 and hasn’t been updated since. It provides a framework for one-way and two-way messaging and allows for the continuation of long-running dialogs. We look at WS-Routing in some more detail in Chapter 14.

Versioning with XSLT

When providing versioning for classes and assemblies, most programmers typically rely on Windows and Visual Studio .NET to do the dirty work. However, when it comes to the task of keeping your other bits and pieces, such as schemas, associated with the Web service so that you can easily tell which is the most up-to-date version, a little more effort is required. Extensible Stylesheet Language Transformations (XSLT) is an ideal tool for versioning because it allows you to write style sheets in XSLT to transform one XML document into another. It is a programming language in its own right; an XSLT processor can take an XML document and an XML source file containing the XSLT document and perform a transformation that renders a new version of the XML document.

The advantage of this approach is that if a request is made to a new version of a Web service and the request is formatted for an old version of the service, instead of throwing out the request you can upgrade it to keep the original request unchanged. Before we do this, we need to look at how XML documents are versioned and then at how to use XSLT transformations to upgrade them.

Versioning XML Documents

XML documents are generally versioned in one of two ways: by adding a version attribute to the XML document or by placing the versioning information within a namespace. You can use XSLT with either approach, or indeed with both approaches at the same time, but the namespace approach is preferable. An example XML fragment shows the difference. The first fragment uses a version attribute:

<?xml version="1.0"?> <album version="1.1">   <title>      <titlename>Rated R</titlename>   </title> </album>

Conceptually, this is the less “pure” method because the version attribute is in essence just tagged onto unrelated code.

The version information is more typically included at the end of a namespace declaration:

<?xml version="1.0"?> <album xmlns="http://www.notashop.com/wscr/albums/1.1">   <title>      <titlename>Rated R</titlename>   </title> </album>

An alternative version using namespaces is also commonly used—instead of using a version number, you include a datestamp as part of the namespace:

<?xml version="1.0"?> <album xmlns="http://www.notashop.com/wscr/albums/2003/03/07">   <title>      <titlename>Rated R</titlename>   </title> </album>

The namespace definition is not without its own drawbacks, though. Any time a namespace is changed, each user of the namespaces is forced to support each slight change in the namespace along with the original namespace. One solution is to not change the namespace URI when only small changes are made to the XML document.

If we have a versioning procedure for our XML documents, then, how do we go about upgrading from one document to another?

Upgrading an XML Document with XSLT

When an upgraded document is released, you will have the old version and an up-to-date version. Accompanying the new version should be an XSLT document that performs the upgrade. First you check the new XML document and version against the old one. You run the XSLT transformation on the original XML document to the next one. Typically, one XSLT transformation handles each incremental upgrade. So one transformation is required to get from version 1.0. to version 1.1. Also, when the transformation is performed, it must change the corresponding version attribute or namespace in the document to reflect this.

An Example Transformation

Let’s say our new version of the service requires a recordLabel element to be supplied as part of a request. This would clearly alter the XML schema and would be an ideal application of an XSLT transformation. This example adds a recordLabel element to the album element:

<xsl:stylesheet version="1.0"    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">   <xsl:output method="xml" version="1.0"      doctype-system="RecordStore.xsd" indent="yes" encoding="utf-8"/>   <xsl:template match="RecordStore">     <RecordStore version="1.1">       <xsl:apply-templates/>     </RecordStore>   </xsl:template>   <xsl:template match="album">     <album>       <recordLabel>false</recordLabel>       <xsl:apply-templates/>     </album>   </xsl:template>   <xsl:template match="*">     <xsl:copy-of select="."/>   </xsl:template> </xsl:stylesheet>

This XSLT program/transformation contains a number of instructions known as templates. The main step of any transformation is to find a rule in the template that matches the root node of the source document. When the template is instantiated, instructions in the template are executed and a results document is created.

Writing XSLT Programs

We can write the XSLT program in two ways: in an ASP style or by using multitemplate transformations.

ASP

XSLT can function much like classic ASP does. ASP dynamically generates new content based on a mixture of HTML and ASP code each time the ASP page is called. XSLT works in a similar way. You can put the XSLT calls between the HTML elements, and XSLT can dynamically generate data by leaving placeholders in the code in the form of curly braces: {}. Dynamic text is generated from the XSLT instructions. XSLT instructions accommodate common programming constructs such as loops and conditional branches. We won’t get into the XSLT syntax here because XSLT is a pretty complex language in its own right.

Unfortunately, this method really only looks after simple transformations, which might be adequate for basic transformations against a known document structure but is not adequate for more complex transformations.

Multitemplate Transformations

Using multitemplate transformations is more complex but provides superior functionality. As you saw earlier in the section titled “An Example Transformation,” you can separate transformations into separate pieces of logic known as templates. You can then apply templates in a procedural programming type of format or even in a declarative style.

You can name and call templates just as you would functions, or you can associate them with a pattern. These patterns are called by the XSLT processor and contain a mixture of static content and XSLT instructions. The syntax for patterns is based on a subset of XPath. The template is placed in the root transformation element.

Again, we won’t go into specific XPath and XSLT syntax—this is just a brief overview of how you might go about upgrading your Web services.




Programming Microsoft. NET XML Web Services
Programming MicrosoftВ® .NET XML Web Services (Pro-Developer)
ISBN: 0735619123
EAN: 2147483647
Year: 2005
Pages: 172

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net