LINQ + WCF + Silverlight (Part Six)


Share

Last time we discussed the concept of a single-operation WCF service contract that provided a message base that we could employ to send one or many application-specific requests to the server and back to the client through a generic implementation. What we didn’t consider was how our application could use something like a GetCustomerInfoRequest message once it is received; after all, we haven’t written code to do any “work” with this message.

To implement message handling, we’ll define an interface that provides the groundwork for our message handling. This basic interface hints at a message lifecycle with before, during, and after execution methods, and a message response that is the end result of that lifecycle.

public interface IMessageHandler
{
    MessageBase Result { get; }

    void BeforeExecute(MessageBase message);
    void Execute(MessageBase message);
    void AfterExecute(MessageBase message);

}

Code Listing – IMessageHandler.cs

The abstract implementation of this interface is a little tricky. First, it is implemented in a way that ensures that the derived type (your own handler) calls its own methods for processing a message, and that service layer dependencies properly register the generic abstract version of the type rather than the concrete type (in other words, we register MessageHandlerBase<GetCustomerInfoRequest> and not GetCustomerInfoHandler).

/// <summary>
/// An abstract implementation of the <see cref="IMessageHandler" /> interface. 
/// Allows execution of the service message handler as well as pre and post events.
/// </summary>
/// <typeparam name="T">The handler processes messages of this type</typeparam>
public abstract class MessageHandlerBase<T> : IMessageHandler where T : MessageBase
{
    #region IMessageHandler Members

    public void BeforeExecute(MessageBase message)
    {
        BeforeExecute((T)message);
    }

    public void Execute(MessageBase message)
    {
        Execute((T) message);
    }

    public void AfterExecute(MessageBase message)
    {
        AfterExecute((T)message);
    }

    #endregion

    public virtual void BeforeExecute(T request) {}
    public abstract void Execute(T request);
    public virtual void AfterExecute(T request) {}

    public void Reply(MessageBase message)
    {
        Result = message;
    }

    #region Implementation of IMessageHandler

    public MessageBase Result { get; private set; }

    #endregion

    /// <summary>
    /// When adding dependencies to our configuration, we want
    /// to register the underlying signature of the generic handler base,
    /// not the custom derived type. This way our service layer doesn't
    /// have to provide its own base class.
    /// </summary>
    public Type AbstractType
    {
        get
        {
            return typeof (MessageHandlerBase<>).MakeGenericType(typeof (T));
        }
    }
}

Code Listing – MessageHandlerBase.cs

As mentioned above, now that we can ask for the generic version of the type rather than the concrete type, we can use our service-layer configuration class to easily set up multiple IMessageHandler dependencies that point to the right place.

private static void AddHandlerDependencies()
{
    var customerInfoHandler = new GetCustomerInfoHandler();
    var updateCustomersHandler = new UpdateCustomersHandler();

    Container.Subscribe<IMessageHandler>(customerInfoHandler.AbstractType, customerInfoHandler);
    Container.Subscribe<IMessageHandler>(updateCustomersHandler.AbstractType, updateCustomersHandler);

}

Code Listing – From ServicesLayerConfiguration.cs

Now we have a way to introduce new handlers and ensure their specific methods are called, we need a way for our service layer to call them. In Part Five, we defined IMessageBus which contained a single method to process any incoming messages. Now, we can inherit that interface to produce a service bus that will process all incoming messages in the services call. This means, of course, that our services layer needs to implement the IMessageBus interface as well.

/// <summary>
/// A service bus provides the base implementation for <see cref="IMessageBus" />,
/// so that service layers only need to declare their own messages and handlers
/// to use them.
/// </summary>
/// <remarks>
/// Attribution: Concept demonstrated by Ayende Rahien
/// Resource: /// www.ayende.com/Blog/archive/2008/03/30/Hibernating-Rhinos-8--Going-Distributed-amp-Building-our-own.aspx
/// </remarks>
public class ServiceBus : IMessageBus
{
    #region Implementation of IMessageBus

    public MessageCollection Process(MessageCollection collection)
    {
        // Create a response collection to send the results
        var results = new MessageCollection
        {
            Messages = new MessageBase[collection.Messages.Length]
        };

        for (var i = 0; i < collection.Messages.Length; i++)
        {
            // Reference the next message in the collection
            var message = collection.Messages[i];

            // Define a generic handler for this message's type
            var type = typeof(MessageHandlerBase<>).MakeGenericType(message.GetType());

            // Find this message's handler in the current dependency container
            var handler = DependencyConfiguration.Container.Retrieve<IMessageHandler>(type);

            // Process this message with the handler
            handler.BeforeExecute(message);
            handler.Execute(message);
            handler.AfterExecute(message);

            // Add the handler result to the response
            results.Messages[i] = handler.Result;
        }

        return results;
    }

    #endregion

}

Code Listing – ServiceBus.cs

As you can see we now have a powerful way of opening up the collection of messages passed through the service, running them through their lifecycle, and collecting responses. There’s room for failure in this class, notably that you must have a dependency subscribed for each and every handler; if you don’t, the attempt to retrieve the handler instance will return a null instance, and this will likely produce a “404 – Not Found” error that bubbles up to the client in a non-intuitive way. If you properly subscribe your dependencies as we did in this example, you should be okay.

Now let’s see what our implementation of the services layer could look like for our application, which uses ‘public’ and ‘private’ services.

[KnownType(typeof(GetCustomerInfoRequest))]
[KnownType(typeof(GetCustomerInfoResponse))]
[KnownType(typeof(UpdateCustomersRequest))]
[KnownType(typeof(UpdateCustomersResponse))]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public partial class Services : IPrivateServices
{
    private readonly ServiceBus _bus = new ServiceBus();

    #region Implementation of IMessageBus

    public MessageCollection Process(MessageCollection messages)
    {
        return _bus.Process(messages);
    }

    #endregion
}

Code Listing – Private/Services.svc.cs In this listing, all we’re doing is implementing IMessageBus, which is predefined in IPrivateServices which we configured in Part Four. We create an instance of ServiceBus to process our messages, and pass them along in the operation call. Notice that we’re declaring “KnownType” attributes in the example above. These are required so that when we generate or update our service references in our Silverlight client application, proxy versions of these classes are created so we can instantiate new requests and pick up new responses when they are returned. There are other ways to define KnownTypes in WCF which you will likely prefer over declaring them here in code. We now have everything we need to send, receive, and process generic messages which are matched with their appropriate handler at runtime and processed accordingly. Finally, let’s look at the GetCustomerInfoHandler class and see what will happen when we process it.

public class GetCustomerInfoHandler : MessageHandlerBase<GetCustomerInfoRequest>
{
    private IRepository<Customer> _customers;

    #region Overrides of MessageHandlerBase<GetCustomerInfoRequest>

    public override void BeforeExecute(GetCustomerInfoRequest request)
    {
        _customers = DependencyConfiguration.Container.Retrieve<IRepository<Customer>>();
    }

    public override void Execute(GetCustomerInfoRequest request)
    {
        var customer = _customers.SingleOrDefault(c => c.CustomerID == request.CustomerId);
        Reply(new GetCustomerInfoResponse {Customer = customer});
    }

    #endregion
}

Code Listing – GetCustomerInfoHandler.cs

All this hard work is starting to pay off! In this handler we’re retrieving our Customer repository from configuration, calling its SingleOrDefault method to retrieve the customer by the ID provided in the request, and returning a response with the retrieved customer instance back to the service, all the while reusing most of our core code and writing very little application-specific code. This is where we want to be.

In the final article you’ll see how to call these services in Silverlight, and how to manage data that’s returning asynchronously.

Article roadmap (and download): LINQ + WCF + Silverlight

Kick It on DotNetKicks.com
blog comments powered by Disqus