S A M P L E S
The client-side asynchronous capabilities of a Web service are defined by the underlying development environment, in this case, .NET. The WSDL toolkit (wsdl.exe) included with the .NET SDK generates two types of asynchronous methods: Begin\End and Async\Completed. Both are completely valid and supported in .NET 2.0, 3.0, and 3.5. Note, using “Add Web Reference” within Visual Studio 2005 will only generate the Async\Completed methods. To construct both, use wsdl.exe on the command line.
The Begin\End
pattern has existed since .NET 1.1 and uses a callback technique for
managing and tracking an asynchronous call. The
Async\Completed
pattern was introduced in .NET 2.0 as an event-driven asynchronous
model. Both techniques initiate a call
to the remote Web service on a threadpool thread. The
asynchronous response to the callback method for the Begin\End pattern
is handled in a threadpool thread. The difference
lies in how the response is handled.
The response handled by Begin's callback method executes on a threadpool
thread and is provided with an IAsyncResult type. In
the callback method, you must explicitly call the End method and provide
the IAsyncResult type. This means you must have
a reference the original proxy type on which to call the method.
The Completed handler executes on the main thread, so managing UI updates
to a Windows form with results from an asynchronous Web service call does
not require that the developer manage cross thread interaction. The
event to a Completed handler contains an IAsyncResult and the object returned
from the method call (so you don't need a reference to the original proxy
type).
Note, both patterns include access to an IAsyncResult which maintains an
IsCompleted property and can be used to check the status of the asynchronous
request. In many cases, the event-driven
model may be easier to implement.
Both patterns are illustrated below via a set of button click events in
a Windows form and a separate custom IAsyncResult class. In general, these
methods were designed for use within desktop clients, but you can use
them in a Web application. In that case you'll
need to explicitly manage synchronicity between the client-Web and Web-service.
In most cases you'll likely need to spawn
a separate thread to use the asynchronous methods.
Form.cs (partial page)
private void button1_Click(object sender, EventArgs e)
{
MapService_MapServer mapservice = new MapService_MapServer();
mapservice.Url = "http://localhost/arcgis/services/MapService/MapServer";
// <Code missing> Construct MapDescription (mapdesc) and ImageDescription (imgdesc)
mapservice.ExportMapImageCompleted += new ExportMapImageCompletedEventHandler(mapservice_ExportMapImageCompleted);
// Simple execution on main thread
mapservice.ExportMapImageAsync(mapdesc, imgdesc);
// Triggers request on threadpool thread
}
// Event handled on main thread
void map_ExportMapImageCompleted(object sender, ExportMapImageCompletedEventArgs e)
{
MapImage mapimg = e.Result;
// Keep Result in Session or other storage, retrieve via another app call.
}
private void button2_Click(object sender, EventArgs e)
{
MapService_MapServer mapservice = new MapService_MapServer();
mapservice.Url = "http://localhost/arcgis/services/MapService/MapServer";
// <Code missing> Construct MapDescription (mapdesc) and ImageDescription (imgdesc)
// Create unique guid as job id for async call
string guid = Guid.NewGuid().ToString();
// Create custom IAsyncResult to store custom properties (map server proxy, job id, etc.)
AsyncDemo.AsyncResult asyncResult = new AsyncDemo.AsyncResult(mapservice, guid, null, null);
// Simple execution on main thread
mapservice.BeginExportMapImage(mapdesc, imgdesc, new AsyncCallback(CallbackMethod), asyncResult);
}
// Method called on threadpool thread
private void CallbackMethod(IAsyncResult result)
{
AsyncDemo.AsyncResult asyncResult = (AsyncDemo.AsyncResult)result.AsyncState;
ESRI.ArcGIS.ADF.ArcGISServer.MapServerProxy mapservice =
(ESRI.ArcGIS.ADF.ArcGISServer.MapServerProxy)asyncResult.ServerProxyInstance;
ESRI.ArcGIS.ADF.ArcGISServer.MapImage mapimg = mapservice.EndExportMapImage(result);
// Keep result in Session or other storage, retrieve via another app call.
}
AsyncDemo.AsyncResult.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
namespace AsyncDemo
{
public class AsyncResult : IAsyncResult, IDisposable
{
System.Web.Services.Protocols.SoapHttpClientProtocol serverProxy;
string jobID;
AsyncCallback callback;
object state;
ManualResetEvent manualResentEvent;
public AsyncResult(System.Web.Services.Protocols.SoapHttpClientProtocol serverProxy, string jobID, AsyncCallback callback, object state)
{
this.serverProxy = serverProxy;
this.jobID = jobID;
this.callback = callback;
this.state = state;
this.manualResentEvent = new ManualResetEvent(false);
}
public AsyncResult(AsyncCallback callback, object state)
{
this.callback = callback;
this.state = state;
this.manualResentEvent = new ManualResetEvent(false);
}
public System.Web.Services.Protocols.SoapHttpClientProtocol ServerProxyInstance
{
get { return serverProxy; }
}
public string JobID
{
get { return jobID; }
}
object IAsyncResult.AsyncState
{
get { return state; }
}
public ManualResetEvent AsyncWait
{
get
{
return manualResentEvent;
}
}
WaitHandle IAsyncResult.AsyncWaitHandle
{
get { return this.AsyncWait; }
}
bool IAsyncResult.CompletedSynchronously
{
get { return false; }
}
bool IAsyncResult.IsCompleted
{
get { return manualResentEvent.WaitOne(0, false); }
}
public void Complete()
{
manualResentEvent.Set();
if (callback != null)
callback(this);
}
public void Dispose()
{
manualResentEvent.Close();
manualResentEvent = null;
state = null;
callback = null;
}
}
}