Asynchronous pattern can be implemented on the service side
by creating operation contracts for asynchronous implementation. To do this,
you can add an OperationContract attribute to your
operations and set its AsyncPattern to true. Apparently
there are two operations in service contract to implement asynchronous pattern
as described above, but you add this attribute to Begin method only even though
both Begin and End methods will be implemented. .NET will take care of this
method internally (as you will see later).
In this article I use a simple example to implement
asynchronous version of a method that gets two integer values and returns the
result of add operation on them.
If I want to implement asynchronous pattern for an Add() service method which normally has a service contract like
Listing 1 and a service class implementation like Listing 2 then I have to
implement this operation in some steps as what you see in this section.
Listing 1: Normal Service Contract
using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
using System.Runtime.Serialization;
namespace AsyncWCF
{
[ServiceContract()]
public interface IAddService
{
[OperationContract]
int Add(int number1, int number2);
}
}
Listing 2: Normal Service Implementation
using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
using System.Runtime.Serialization;
using System.Threading;
namespace AsyncWCF
{
public class AddService : IAddService
{
#region IAddService Members
public int Add(int number1, int number2)
{
return (number1 + number2);
}
#endregion
}
}
Let me begin implementing this method with the asynchronous
pattern.
First step is to create a service contract. As I stated
above, I have to include two methods for my Add()
method in asynchronous version: BeginAdd() and EndAdd() and first method should be marked with an OperationContract attribute with AsyncPattern
property set to true (Listing 3).
Listing 3: Service Contract
using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
using System.Runtime.Serialization;
namespace AsyncWCF
{
[ServiceContract()]
public interface IAddService
{
[OperationContract(AsyncPattern = true)]
IAsyncResult BeginAdd(int number1, int number2,
AsyncCallback callback, object state);
int EndAdd(IAsyncResult ar);
}
}
At this point, I should implement IAsyncResult
to use it in my code. It is common to implement this interface in a class in the
general form then derive a class with this newly created base class based on
project requirements. So, first I create an AsyncResult
base class and implement IAsyncResult for it. I also
implement IDisposable to manage my resources better.
This implementation does not have anything related to my project. It just
implements a logic that can be used in other projects as well. The final code
for the AsyncResult class is shown in Listing 4.
Listing 4: AsyncResult Class
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
namespace AsyncWCF
{
public class AsyncResult : IAsyncResult, IDisposable
{
AsyncCallback callback;
object state;
ManualResetEvent manualResentEvent;
public AsyncResult(AsyncCallback callback, object state)
{
this.callback = callback;
this.state = state;
this.manualResentEvent = new ManualResetEvent(false);
}
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;
}
}
}
Let me describe some points about the above code. AsyncResult has some properties, like AsyncCallback,
state object and a ManualResetEvent, which handle waiting
for asynchronous operation. There is an IsCompleted
property which returns a Boolean value to specify that if the operation is
completed asynchronously, a simple WaitOne(0, false) method
call always returns the appropriate value for any asynchronous operation.
The last point is about Complete()
method. Here I call the Set() method of my ManualResetEvent
to show that my event is signaled and any other awaiting thread can follow. Then
I pass the current object to callback if callback is not null.
After this, I create an AddAsyncResult
class and inherit it from AsyncResult to add a few
properties for my requirements and use them later. It just keeps a few values
as the first and second numbers and the result of performing add operation on
them (Listing 5).
Listing 5: AddAsyncResult Class
using System;
using System.Collections.Generic;
using System.Text;
namespace AsyncWCF
{
internal class AddAsyncResult : AsyncResult
{
public readonly int number1 = 0;
public readonly int number2 = 0;
private int result;
public int Result
{
get { return result; }
set { result = value; }
}
public AddAsyncResult(int num1, int num2,
AsyncCallback callback, object state)
: base(callback, state)
{
this.number1 = num1;
this.number2 = num2;
}
}
}
Alright, now I have created what I needed to begin
implementing my service logic. Here I write my code logic for the service.
In the BeginAdd() method I create
an instance of AddAsyncResult by passing appropriate
parameters to it then call ThreadPool.QueueUserWorkItem()
method to add a method to waiting queue for execution. This method will run
whenever a thread becomes available. The logic for my CallbackMethod
will be discussed later. BeginAdd() returns my AddAsyncResult object as the result. Listing 6 shows the
code of BeginAdd() method.
Listing 6: BeginAdd() Method
public IAsyncResult BeginAdd(int number1, int number2,
AsyncCallback callback, object state)
{
AddAsyncResult asyncResult =
new AddAsyncResult(number1, number2, callback, state);
ThreadPool.QueueUserWorkItem(new WaitCallback
(Callback), asyncResult);
return asyncResult;
}
In the Callback(), which is
presented in Listing 7, I simply convert the passed object to an AddAsyncResult
and try to set its Result property to the returned value of InternalAdd()
operation (will be shown in a moment). At the end, I call the Complete() method to release the thread and allow the other
operations to execute.
Listing 7: Callback() Method
private void Callback(object state)
{
AddAsyncResult asyncResult = state as AddAsyncResult;
try
{
asyncResult.Result = InternalAdd(asyncResult.number1,
asyncResult.number2);
}
finally
{
asyncResult.Complete();
}
}
Logic of InternalAdd() is very
very simple and what you can expect from an Add operation. But it also puts a
short delay (15 seconds) before adding numbers to make the execution longer
(Listing 8).
Listing 8: InternalAdd() Method
private int InternalAdd(int number1, int number2)
{
Thread.Sleep(15000);
return number1 + number2;
}
And finally, there is the EndAdd()
method (Listing 9). Here I convert passed IAsyncResult
parameter to an AddAsyncResult object and call the WaitOne() method of its ManualResetEvent
to wait until the current execution ends then return the Result
property.
Listing 9: EndAdd() Method
public int EndAdd(IAsyncResult ar)
{
int result = 0;
if (ar != null)
{
using (AddAsyncResult asyncResult = ar as AddAsyncResult)
{
if (asyncResult == null)
throw new ArgumentNullException(
"IAsyncResult parameter is null.");
asyncResult.AsyncWait.WaitOne();
result = asyncResult.Result;
}
}
return result;
}
So far so good! I have written my service completely. Now
I deploy this service to IIS and host it there in order to use SvcUtil command
to generate a proxy class and configuration file for my client. For more
information about hosting WCF services you can read my article entitled Start
Development with Windows Communication Foundation.