UPDATE: not a bug
You got to love the internet. Minutes after I wrote about what I thought was a bug in WPF,
Joseph Cooney pointed me to
this page which explains how to save the cookie on the client to avoid losing the SessionID. Seems that I have been spoilt by my years of ASP.NET programming, where the browser stores the cookies automatically. I will update the example, and will post again about this soon!
Problem
This
prototype demonstrates that, in WPF, the SessionID used when calling web services is not saved by default.
Using SessionID in web services
When a client calls a web service the first time, it is assigned a unique identifier called SessionID, which is saved using cookies. When the following calls arrive, the SessionID is read from the cookies collection, and the Session object (
HttpSessionState) is retrieved. This object can be used to store state information related with the calling client.
In order to make the Session object accessible to web methods, the "WebMethod" attribute decorating the web method uses the
EnableSession attribute, which must be set to true.
[WebService( Namespace = "http://www.galasoft-lb.ch/" )]
[WebServiceBinding( ConformsTo = WsiProfiles.BasicProfile1_1 )]
[ToolboxItem( false )]
public class Service1 : System.Web.Services.WebService
{
[WebMethod( EnableSession=true )]
public string GetSessionId()
{
return Session.SessionID;
}
}
Web method accessing the Session
Using web services in WPF
Windows Presentation Foundation (WPF) can consume ASMX web services easily, because they relay on the .NET framework. In .NET 2.0, using a web service is as easy as adding a Web Reference to the project (Right-click on the project / Add Web Reference...). Enter the URL of the ASMX file, and select the service you want to use.
Once this is done, the web service is available as a proxy, meaning that calling a method on the proxy object will automatically send the web service request, wait for the answer and parse the response, providing the client with the corresponding result.
Web methods can be called synchronously and asynchronously, but the async mode is better, because it doesn't block the client while the request is sent and processed. The client simply registers a method to the "Completed" event of the corresponding method. In Visual Studio 2005, this is very easy to do: Select the "Completed" event you want to register with, then type "+=". This presents you with a dialog asking if you want Studio to insert the corresponding code. Press "Tab" to confirm, and then "Tab" again to declare the method.
private void bnGetSessionId_Click( object sender, RoutedEventArgs e )
{
try
{
GetSessionIdService.Service1 oSessionIdService
= new GetSessionIdService.Service1();
oSessionIdService.GetSessionIdCompleted
+= new GetSessionIdService.GetSessionIdCompletedEventHandler(
oSessionIdService_GetSessionIdCompleted );
oSessionIdService.GetSessionIdAsync();
}
catch ( Exception ex )
{
lblSessionId.Text = ex.Message;
lblSessionId.Foreground = Brushes.Red;
}
}
Calling the wen method asynchronously
The only step remaining now it to handle the response, in the corresponding event handler.
private void oSessionIdService_GetSessionIdCompleted( object sender,
GetSessionIdService.GetSessionIdCompletedEventArgs e )
{
if ( e != null
&& e.Result != null
&& e.Result.Length > 0 )
{
if ( lblSessionId.Text.Length > 0 )
{
lblSessionId.Text += Environment.NewLine;
}
lblSessionId.Text += "SessionID at "
+ DateTime.Now.ToLongTimeString()
+ ": " + e.Result;
lblSessionId.Foreground = Brushes.Black;
}
else
{
lblSessionId.Text = "Unknown error";
lblSessionId.Foreground = Brushes.Red;
}
}
Processing the Response
Consequences
Many web services use the Session object to store state information. Most web application nowadays are stateful to enable a better user experience. It is therefore necessary to save the SessionID on the WPF client when using web services, or else on every request, a new Session object is created. My
prototype demonstrates the fact: Every time you call the service, it returns a different session ID.
In order to save the SessionID in a cookie on the client, a CookieContainer must be used. This
page at MSDN shows how.
Different session ID on every call