About company Our development services Portfolio. Case studies Support Testimonials Contacts Brutka Ltd: web site development and custom software development

Information technology news archive


Foundations: Build a Queued WCF Response Service

Windows Communication Foundation (WCF) enables disconnected work between clients and services. The client posts messages to a queue and the service processes them later. Such interaction permits a different programming model from the default request/response pattern, enabling possibilities such as load leveling, improved availability, and compensating work, to name just a few. This column starts with a brief introduction to Windows® Communication Foundation queued calls and then presents an interesting problem-how to get results out of a queued call-and the solution via some cool Windows Communication Foundation programming techniques and a few helper classes I wrote for this purpose.
Queued Calls
Windows Communication Foundation provides support for queued calls using NetMsmqBinding. Instead of transporting a message over TCP or HTTP, Windows Communication Foundation transports the message over Microsoft® Message Queuing (MSMQ). Instead of sending the Windows Communication Foundation message to a live service, the client posts the message to an MSMQ queue. All the client sees and interacts with is the queue, not a service endpoint. As a result, the calls are inherently asynchronous and disconnected. The calls will execute when the service processes the messages at some time in the future.
Note that there is no direct mapping of Windows Communication Foundation messages to MSMQ messages. A single MSMQ message can contain multiple Windows Communication Foundation messages or just a single one according to the contract session mode. With a required session mode, multiple Windows Communication Foundation calls can be in one MSMQ message, and with the allowed or not allowed session modes (used by per-call and singleton services), each Windows Communication Foundation call will be in a separate MSMQ message.
As with every Windows Communication Foundation service, the client interacts with a proxy. Since the proxy is configured to use the MSMQ binding, it does not send the Windows Communication Foundation message to any particular service. Instead, it converts the calls to MSMQ messages and posts them to the queue specified in the endpoint's address.
On the service side, when a service host with a queued endpoint is launched, the host installs a queue listener. The queue listener detects that there is a message in the queue, de-queues the message, then creates the host side's chain of interceptors ending with a dispatcher. The dispatcher calls the service instance as usual. If multiple messages are posted to the queue, the listener can create new instances as the messages come off the queue, thus ending with asynchronous, disconnected, and concurrent calls.
If the host is offline, messages will simply be pending in the queue. The next time the host is up, the messages will be forwarded to the service.
A potentially disconnected call made against a queue cannot possibly return any values because no service logic is invoked at the time the message is dispatched to the queue. In addition, the call may be dispatched to the service and processed after the client application has shut down, when there is no client available to process the returned values. Similarly, the call can't return to the client any service-side exceptions, and there may not be a client around to catch and handle the exception. Since the client cannot be blocked by invoking the operation-or rather, the client is only blocked for the briefest moment it takes to queue up the message-queued calls are inherently asynchronous from the client's perspective. These are the classic characteristics of one-way calls. Consequently, any contract exposed by an endpoint that uses the NetMsmqBinding can only have one-way operations. Windows Communication Foundation verifies this at the service and proxy load time:
//Only one-way calls on queued contracts
[ServiceContract]
interface IMyContract
{
[OperationContract(IsOneWay = true)]
void MyMethod();
}
Since the interaction with MSMQ is encapsulated in the binding, there is nothing in the service or client invocation code pertaining to the fact that the call is queued. The service and client code look like any other Windows Communication Foundation client and service code.
When defining an endpoint for a queued service, the endpoint address must contain the queue's name and the type of the queue (public or private):
<endpoint
address = "net.msmq://localhost/private/
MyServiceQueue"
binding = "netMsmqBinding"
...
/>
Finally, MSMQ is a Windows Communication Foundation transactional resource manager. If the queue is transactional, a message posted by the client will be rolled back if the client's transaction aborts. On the service side, reading the message from the queue kick-starts a new transaction. If the service participates in that transaction and the service aborts the transaction (perhaps as a result of an exception), then the message will roll back into the queue for another retry. Windows Communication Foundation offers elaborate failure detection and poison message handling support that is beyond the scope of this column.

Response Service
The programming model of queued calls described so far is one-sided: the client posts a one-way message to a queue, and the service processes that message. This model is sufficient when the queued operations are one-way calls by design. However, the queued service may need to report back to its client on the result of the invocation, returned results, and even errors. By default, however, this is impossible. Windows Communication Foundation equates queued calls with one-way calls, which inherently forbid any such response. In addition, queued services (and their clients) are potentially disconnected. If the client posts a queued call to a disconnected service, when the service finally gets the messages and processes them there may not be a client to return the values to because the client may be long gone. The solution is to have the service report back to a client-provided queued service. I call such a service a response service.
The response service is just another queued service in the system. It may be disconnected from the client as well and hosted in a separate process or a separate machine, or it can share the client's process. If the response service shares the client's process, when the client is launched the response service will start processing the queued responses. Having the response service in a separate process (or even on a separate machine) from the client helps to further decouple the lifetime of the response service from the clients that use it.

Designing a Response Service Contract
As with the use of any Windows Communication Foundation service, the client and the service need to agree beforehand on the response contract and what it will be used for, such as returned values and error information or just returned values. Note that you can also split the response service into two services and have one for results and another for errors. For example, consider the ICalculator contract implemented by the queued MyCalculator service, as shown here:
[ServiceContract]
interface ICalculator
{
[OperationContract(IsOneWay = true)]
void Add(int number1,int number2);
... //More operations
}
[ServiceBehavior(InstanceContextMode =
InstanceContextMode.PerCall)]
class MyCalculator : ICalculator {...}
The MyCalculator service is required to respond to its client with the result of the calculation and to report any errors. The result of the calculation is an integer, and the error is in the form of the Windows Communication Foundation ExceptionDetail data contract. For the response service, you could define the ICalculatorResponse contract like this:
[ServiceContract]
interface ICalculatorResponse
{
[OperationContract(IsOneWay = true)]
void OnAddCompleted(int result,ExceptionDetail error);
}
The response service supporting ICalculatorResponse needs to examine the returned error information, notify the client app, the user, or the application administrator on the method completion, and make the results available to the interested parties. Here is a simple response service that supports ICalculatorResponse:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
class MyCalculatorResponse : ICalculatorResponse
{
[OperationBehavior(TransactionScopeRequired = true)]
public void OnAddCompleted(int result,ExceptionDetail error)
{
MessageBox.Show("result = " + result,"MyCalculatorResponse");
if(error != null)
{
//Handle error
}
}
}
There are two immediate problems with the implementation of both MyCalculator and MyCalculatorResponse. The first is that the same response service could be used to handle the response (or completion) of multiple calls on multiple queued services, and yet MyCalculatorResponse (and more importantly the clients it serves) has no way of distinguishing among responses. The solution is to have the client that issued the original queued call tag the call by associating it with some unique ID. The queued service MyCalculator needs to pass that ID to MyCalculatorResponse so that it could apply some custom logic regarding that ID.
The second problem is the queued service discovering the address of the response service. Unlike duplex callbacks, there is no built-in support in Windows Communication Foundation for passing the response service reference to the service. Placing that address in the service host config file (in a client section) is not a good idea because the same queued service could be called by multiple clients, each with its own dedicated response service and address.
One possible solution is to explicitly pass both the client-managed ID and the desired response service address as parameters to every operation on the queued service contract:
[ServiceContract]
interface ICalculator
{
[OperationContract(IsOneWay = true)]
void Add(int number1,int number2,
string responseAddress,string methodID);
}
In much the same way, the queued service could explicitly pass the method ID to the response service as a parameter to every operation on the queued response contract:
[ServiceContract]
interface ICalculatorResponse
{
[OperationContract(IsOneWay = true)]
void OnAddCompleted(int result,ExceptionDetail error,
string methodID);
}

Using Message Headers
While passing the address and the ID as explicit parameters would work, it does distort the original contract, and introduces plumbing-level parameters along business-level parameters in the same operation. A better solution is to have the client store the response address and operation ID in the outgoing message headers of the call. Using the message headers this way is a general-purpose technique for passing out-of-band information to the service, information that is otherwise not present in the service contract.
The operation context offers collections of incoming and outgoing headers, available with the IncomingMessageHeaders and OutgoingMessageHeaders properties:
public sealed class OperationContext : ...
{
public MessageHeaders IncomingMessageHeaders {get;}
public MessageHeaders OutgoingMessageHeaders {get;}
... //More members
}
Each collection is of the type MessageHeaders, which represents a collection of MessageHeader objects:
public sealed class MessageHeaders : IEnumerable<...>, ...
{
public void Add(MessageHeader header);
public T GetHeader<T>(int index);
public T GetHeader<T>(string name,string ns);
... //More members
}
The class MessageHeader is not intended for application developers to interact with directly. Instead, use the MessageHeader<T> class which provides type safety and easy conversion from a common language runtime (CLR) type to a message header:
public abstract class MessageHeader : ...
{...}
public class MessageHeader<T>
{
public MessageHeader();
public MessageHeader(T content);
public T Content {get;set;}
public MessageHeader GetUntypedHeader(string name,string ns);
... //More members
}
As the type parameter for MessageHeader<T> you can use any serializable or data contract type. You can construct a MessageHeader<T> around a CLR type, and then use the GetUntypedHeader method to convert it to a MessageHeader and store it in the outgoing headers. GetUntypedHeader requires you to provide the generic type parameter name and namespace. The name and namespace will be used to look up the header from the header collection. You perform the lookup via the GetHeader<T> method of MessageHeaders. Calling GetHeader<T> obtains the value of the type parameter of the MessageHeader<T> used.

The ResponseContext Class
Since the client needs to pass both the address and the method ID in the message headers, a single primitive type parameter will not do. Instead, use my ResponseContext class, which is shown in Figure 4 with some of the error-handling code omitted.
ResponseContext provides a place to store both the response address and the ID. In addition, if the client would like to use a separate response service for faults, ResponseContext also provides a field for the fault response service address.
The client is responsible for constructing an instance of ResponseContext with a unique ID. While the client can supply that ID as a construction parameter, the client could also use the constructor of ResponseContext, which takes just the response address, and have that constructor generate a GUID for the ID. The important property of ResponseContext is the static Current property. It completely encapsulates the interaction with the message headers. By accessing the Current property you have a nice programming model: the get accessor reads from the incoming message headers the instance of ResponseContext, and the set accessor stores in the outgoing headers an instance of a ResponseContext.

Client-Side Programming
The client can provide an ID per method call by using a different instance of ResponseContext for each call. The client needs to store an instance of ResponseContext in the outgoing message headers. In addition, the client must do so in a new operation context; it can't use its existing operation context. This requirement is true for service and non-service clients alike. Windows Communication Foundation enables a client to adopt a new operation context for the current thread with the OperationContextScope class defined as follows:
public sealed class OperationContextScope : IDisposable
{
public OperationContextScope(IContextChannel channel);
public OperationContextScope(OperationContext context);
public void Dispose();
}
OperationContextScope is a general technique for spinning a new context when the one you have is inadequate. The constructor of OperationContextScope replaces the current thread's operation context with the new context. Calling Dispose on the OperationContextScope instance restores the old context (even if it was null). If you do not call Dispose, that may damage other objects on the same thread that expect the previous context. As a result, OperationContextScope is designed to be used inside a using statement and to provide only a scope of code with a new operation context, even in the face of exceptions.
When constructing a new OperationContextScope instance, you provide its constructor with the inner channel of the proxy used for the call. The client needs to create a new OperationContextScope, and inside the scope assign to ResponseContext.Current.
To streamline and automate the client's work, you need a proxy base class that encapsulates the response service setup steps of Figure 5. Unlike with duplex callback, Windows Communication Foundation does not provide such a proxy class, so you have to hand-craft one. To ease that task, I wrote the ResponseClientBase<T> defined in Figure 6.
To use ResponseClientBase<T>, derive a concrete class out of it, and provide the queued contract type for the type parameter. Unlike the practice for a normal proxy, do not have the subclass also derive from the contract. Instead, provide a similar set of methods that all return a string for the method ID, not void. (This is why you can't derive from the contract, because the operations on the contract do not return anything-they are all one-way). For example, Figure 7 shows the matching response-service-aware proxy using this queued service contract:
[ServiceContract]
interface ICalculator
{
[OperationContract(IsOneWay = true)]
void Add(int number1,int number2);
... //More operations
}
Using ResponseClientBase<T>, the code shown in Figure 5 is reduced to this:
string responseAddress =
"net.msmq://localhost/private/MyCalculatorResponseQueue";
CalculatorClient proxy = new CalculatorClient(responseAddress);
string methodId = proxy.Add(2,3);
proxy.Close();
The virtual method GenerateMethodId of ResponseClientBase<T> uses a GUID for a method ID. Your subclass of ResponseClientBase<T> can override it and provide any other unique string, such as an incremented integer.
The constructors of ResponseClientBase<T> accept the response address and the regular proxy parameters such as endpoint name, address and binding. The constructors store the response address in a read-only public field. ResponseClientBase<T> derives from a regular ClientBase<T>, and so all constructors delegate to their respective base constructor.
The heart of ResponseClientBase<T> is the Enqueue method. Enqueue accepts the name of the operation to invoke (actually queue a message to) and the operation parameters. Enqueue creates a new operation context scope, generates a new method ID, and stores the ID and the response address in a ResponseContext. It assigns the ResponseContext to the outgoing message headers by setting ResponseContext.Current and then uses reflection to invoke the provided operation name. Due to the use of reflection and late binding, ResponseClientBase<T> does not support contract hierarchy or overloaded operations.

Service-Side Programming
The queued service accesses its incoming message headers collection via ResponseContext.Current and reads from it the response address and the method ID. The service needs the address so that it can construct the proxy to the response service, and the ID so that it can provide that ID to the response service. The service usually has no direct use for the ID.
The service can use the same technique as the client to pass the method ID to the response service: instead of explicit parameters, use the outgoing message headers to pass the ID out-of-band to the response service. Much like the client, the service must use a new operation context via OperationContextScope to be able to modify the outgoing headers collection. The service can programmatically construct a proxy to the response service and provide the proxy with the response address and an instance of NetMsmqBinding. The service can even read the binding settings from the config file.
The service catches all exceptions thrown by the business logic operation and wraps each exception with an ExceptionDetail object. The service does not re-throw the exception because that would also cancel the response as it will abort the transaction that queues the response message to the response service queue.
In the finally statement, regardless of exceptions, the service responds. It constructs a proxy to the response service using the address from the response context and a new instance of NetMsmqBinding. The service uses the proxy's inner channel to seed a new OperationContextScope. Inside the new scope the service adds to the new context's outgoing headers the same response context it received by setting Response.Current and calls the response service proxy, in effect queuing the response. The service then disposes of the context scope and closes the proxy.
To streamline and automate the work required by the service to extract the response parameters from the headers and to respond, I created the ResponseScope<T> class:
public class ResponseScope<T> : IDisposable where T : class
{
public readonly T Response;
public ResponseScope();
public ResponseScope(string bindingConfiguration);
public ResponseScope(NetMsmqBinding binding);
public void Dispose();
}
ResponseScope<T> is a disposable object-it installs a new operation context, and when it is disposed of, the scope restores the old operation context. To automate that even in the face of exceptions, ResponseScope<T> should be used in a using statement.
ResponseScope<T> takes a type parameter representing the response contract, and it offers the Response a read-only public field of the same type. Response is a proxy to the response service and the clients of ResponseScope<T> use Response to call operations on the response service. There is no need to dispose of Response because ResponseScope<T> does that in Dispose. ResponseScope<T> will instantiate Response based on the default endpoint in the config file or using the binding information (the config section name or an actual binding instance) provided to the constructor. Figure 9 shows the implementation of ResponseScope<T>.
Using ResponseScope<T> the finally statement of Figure 8 is reduced to this:
finally
{
using(ResponseScope<ICalculatorResponse> scope =
new ResponseScope<ICalculatorResponse>())
{
scope.Response.OnAddCompleted(result,error);
}
}
The constructor of ResponseScope<T> uses ResponseContext.Current to extract the incoming response context. It then uses a channel factory to initialize Response with a proxy to the response service. The trick in implementing ResponseScope<T> is not using OperationContextScope inside a using statement. By constructing a new OperationContextScope, ResponseScope<T> establishes a new operation context. ResponseScope<T> encapsulates an OperationContextScope and saves the newly created OperationContextScope. The old context will be restored once ResponseScope<T> is disposed of in a using statement. ResponseScope<T> then uses ResponseContext.Current to add to the new operation context's outgoing headers the response context and returns.

Response Service Programming
The response service accesses its incoming message headers collection, reading from it the method ID and responding accordingly. Figure 10 demonstrates a possible implementation of such a response service.
The response service accesses its operation context incoming headers via ResponseContext.Current and, just like the queued service, it extracts the method ID and handles the completion or the error.

Conclusion
Queued services are instrumental in many applications. If being able to report completion, errors, and results to clients is essential to your application, the helper classes in this column augment the basic offering of Windows Communication Foundation and provide the missing support in a line or two of code.
The Windows Communication Foundation techniques shown in this column, such as interacting with message headers, replacing the operation context, writing custom proxy base classes and custom contexts are useful in many other cases, especially with custom frameworks.

2007-03-27

 

More IT news, prices and information