GOTCHA 63 Web-service proxy may fail when used for multiple asynchronous calls


GOTCHA #63 Web-service proxy may fail when used for multiple asynchronous calls

Say you are interested in making multiple independent requests to a web service that has a method with a delayed response. You can certainly take advantage of the asynchronous access that the service proxy generated by wsdl.exe/Visual Studio provides. However, if you want to make more than one request at the same time, should you use the same proxy instance or different instances?

Taking a closer look at the BeginInvoke() and EndInvoke() methods of the proxy, it is clear that when you call BeginInvoke(), you are given an IAsyncResult handle. When you call EndInvoke(), you send this handle to it and get the result for that specific invocation. Doesn't that suggest that you can make multiple asynchronous requests using a single instance of the proxy?

As it turns out, you can. However, you need to understand some issues from the web server's point of view.

I have been asked this question quite a few times. I even ran into this situation inadvertently, and came to the realization illustrated in Examples 7-32 through 7-35.

Example 7-32. Multiple calls on a web service (C# server side)

C# (MultipleWSCalls), server side

 //MyService.asmx.cs part of ACSWSForMultiRequest.dll (Web Service) using System; using System.Collections; using System.ComponentModel; using System.Data; using System.Diagnostics; using System.Web; using System.Web.Services; namespace ACSWSForMultiRequest {     [WebService(Namespace="http://www.ACSWSForMultiRequest.com")]     public class MyService : System.Web.Services.WebService     {         /// ...         [WebMethod]         public string Method1(int val)         {             System.Threading.Thread.Sleep(5000);             return val.ToString();         }     } } 

Example 7-33. Multiple calls on a web service (C# client side)

C# (MultipleWSCalls), client side

 ///Test.cs part of AClient.exe using System; using AClient.ACSWSForMultiRequest; namespace AClient {     class Test     {         [STAThread]         static void Main(string[] args)         {             MyService service = new MyService();             service.Credentials =                 System.Net.CredentialCache.DefaultCredentials;             Console.WriteLine(                 "Making synchronous request at {0}",                 DateTime.Now.ToLongTimeString());             Console.WriteLine("Recevied {0} at {1}",                 service.Method1(0),                 DateTime.Now.ToLongTimeString());             Console.WriteLine("Making two requests at {0}",                 DateTime.Now.ToLongTimeString());             service.BeginMethod1(1, new AsyncCallback(display),                 service);             service.BeginMethod1(2, new AsyncCallback(display),                 service);             Console.ReadLine();         }         private static void display(IAsyncResult handle)         {             MyService theService =                 handle.AsyncState as MyService;             string result = theService.EndMethod1(handle);             Console.WriteLine("Result {0} received {1}",                 result, DateTime.Now.ToLongTimeString());         }     } } 

Example 7-34. Multiple calls on a web service (VB.NET server side)

VB.NET (MultipleWSCalls), server side

 'MyService.asmx.vb part of AVBWSForMultiRequest.dll (Web Service) Imports System.Web.Services <System.Web.Services.WebService(Namespace := _    "http://tempuri.org/AVBWSForMultiRequest/MyService")> _ Public Class MyService     Inherits System.Web.Services.WebService         '...     <WebMethod()> _     Public Function Method1(ByVal val As Integer) As String         System.Threading.Thread.Sleep(5000)         Return val.ToString()     End Function End Class 

Example 7-35. Multiple calls on a web service (VB.NET client side)

VB.NET (MultipleWSCalls), client side

 ''Test.vb part of AClient.exe Imports AClient.AVSWSForMultiRequest Module Test     Sub Main()         Dim service As New MyService         service.Credentials = _          System.Net.CredentialCache.DefaultCredentials         Console.WriteLine( _            "Making synchronous request at {0}", _            DateTime.Now.ToLongTimeString())         Console.WriteLine("Recevied {0} at {1}", _          service.Method1(0), _          DateTime.Now.ToLongTimeString())         Console.WriteLine("Making two requests at {0}", _          DateTime.Now.ToLongTimeString())         service.BeginMethod1(1, _             New AsyncCallback(AddressOf display), service)         service.BeginMethod1(2, _             New AsyncCallback(AddressOf display), service)         Console.ReadLine()     End Sub     Private Sub display(ByVal handle As IAsyncResult)         Dim theService As MyService = _          CType(handle.AsyncState, MyService)         Dim result As String = theService.EndMethod1(handle)         Console.WriteLine("Result {0} received {1}", _          result, DateTime.Now.ToLongTimeString())     End Sub End Module 

In this example, the web service has a method Method1() that has an artificial delay to simulate some activity. The client first makes one synchronous call, to which it gets a response after five seconds. (The reason you see an additional two-second delay on the first call is that the web service warms up when it receives the first request.) Then the client makes two asynchronous calls, one right after the other. The output from the above program is shown in Figure 7-30.

Figure 7-30. Output from Example 7-32


No problemso far. Now, let's say the service maintains state and expects the client code to set the proxy's CookieContainer property, as shown in Example 7-36.

Example 7-36. Making multiple calls when session is maintained

C# (MultipleWSCalls)

 //MyService.asmx.cs part of ACSWSForMultiRequest.dll (Web Service) ...         [WebMethod(true)]         public string Method1(int val)         {             System.Threading.Thread.Sleep(5000);             return val.ToString();         } ... //Test.cs. part of AClient.exe ...             MyService service = new MyService();             service.CookieContainer =                 new System.Net.CookieContainer();             service.Credentials =                 System.Net.CredentialCache.DefaultCredentials; ... 

VB.NET (MultipleWSCalls)

 'MyService.asmx.vb part of AVBWSForMultiRequest.dll (Web Service) ...     <WebMethod(True)> _     Public Function Method1(ByVal val As Integer) As String         System.Threading.Thread.Sleep(5000)         Return val.ToString()     End Function ... 'Test.vb part of AClient.exe ...         Dim service As New MyService         service.CookieContainer = _          New System.Net.CookieContainer         service.Credentials = _          System.Net.CredentialCache.DefaultCredentials ... 

In this code you have made one change to the web service. In the WebMethod attribute, you have set the parameter (true) to enable session management. You have also set the CookieContainer property on the client proxy to a non-null reference. Let's run the application now and see what the response is. The output is shown in Figure 7-31.

Figure 7-31. Output from Example 7-36


Even though the two asynchronous requests are made simultaneously, the responses are separated by a five-second delay. This is because on the server side the requests get serialized. The web service recognizes the session id from the cookie that the proxy transmits and determines that the two requests belong to the same session. As a result, in order to avoid any threading conflicts, it serializes the requests, allowing only one request belonging to a session to execute at a time.

IN A NUTSHELL

When you send multiple concurrent requests to a web service, do not use the same proxy instance if you set the CookieContainer property on the proxy, or if session state is supported by the web service. Doing so causes the requests to execute consecutively on the server, rather than concurrently. If consecutive behavior is what you want, then this is a good approach.

SEE ALSO

Gotcha #58, "Threads from the thread pool are scarce" and Gotcha #61, "Exceptions thrown from threads in the pool are lost."



    .NET Gotachas
    .NET Gotachas
    ISBN: N/A
    EAN: N/A
    Year: 2005
    Pages: 126

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