The Inquisitive Coder – Davy Brion's Blog

Trying to walk that thin line between intelligence and ignorance

Batching WCF calls

Posted by Davy Brion on June 26th, 2008

Update: i also have a complete series of posts on this which you may find interesting.

What happens if you have the following code client-side:

            View.ProductCategories = service.GetProductCategories();

            View.Products = service.GetProductOverviews();

The service variable is a reference to a WCF service proxy. This means that this causes 2 remote service calls. I really hate unnecessary/excessive roundtrips, so i would rather retrieve this data from the service in one roundtrip. Unfortunately, this is not something that is supported by WCF out of the box, so if you want to do this you have to do it yourself. There are a couple of options, so i started browsing the web. Once again, it’s Ayende to the rescue. The approach he suggests is a bit cumbersome compared to how you’d normally design your services, but it does offer a great deal of flexibility and it allows you to nicely batch your requests. So i figured i should try implementing this approach.

The idea is to provide a service method that looks like this:

        Response[] Process(params Request[] requests);

Each Request corresponds to what used to be a normal service method, and this method returns a Response for each Request. In Ayende’s post, he suggest offering this as the only service method. I won’t go that far though. I’ll offer this method, and each ‘normal’ method allthough those methods will also use Request types as input variables and Response types as return values. The idea is to be able to use each service method individually, or to have multiple service methods executed in one roundtrip through the Process method.

We need a way to define Requests and Responses, so we create the following classes:

    [Serializable]

    public abstract class Response { }

 

    [Serializable]

    public abstract class Request {}

For now, these are empty but i’m pretty sure i’ll add a few members to these later on. Then we define specific Request and Response types for the functionality our service needs to offer:

    [Serializable]

    public class GetProductCategoriesRequest : Request {}

 

    [Serializable]

    public class GetProductOverviewsRequest : Request

    {

        public int? ProductCategoryId { get; set; }

    }

    [Serializable]

    public class GetProductCategoriesResponse : Response

    {

        public ProductCategoryDTO[] ProductCategories { get; private set; }

 

        public GetProductCategoriesResponse(ProductCategoryDTO[] productCategories)

        {

            ProductCategories = productCategories;

        }

    }

 

    [Serializable]

    public class GetProductOverviewsResponse : Response

    {

        public ProductOverviewDTO[] Products { get; private set; }

 

        public GetProductOverviewsResponse(ProductOverviewDTO[] products)

        {

            Products = products;

        }

    }

We then create a base interface that each service will implement (along with more specific service interfaces):

    [ServiceContract]

    public interface IService : IDisposable

    {

        /// <summary>

        /// Processes each request within the same service call

        /// </summary>

        /// <param name="requests">each request</param>

        /// <returns>the corresponding responses</returns>

        [OperationContract]

        [ServiceKnownType("GetKnownTypes", typeof(KnownTypeProvider))]

        Response[] Process(params Request[] requests);

    }

The KnownTypeProvider is responsible for providing a list of types that should be treated as KnownTypes during serialization/deserialization. We could also list each type that derives from Response/Request but that will become a long list, so i went for this approach. The code of the KnownTypeProvider class looks like this:

    public static class KnownTypeProvider

    {

        private static readonly List<Type> knownTypes;

 

        static KnownTypeProvider()

        {

            knownTypes = BuildListOfKnownTypes();

        }

 

        private static List<Type> BuildListOfKnownTypes()

        {

            Assembly containingAssembly = typeof(KnownTypeProvider).Assembly;

 

            return containingAssembly.GetTypes()

                .Select(t => t)

                .Where(t => t.IsSubclassOf(typeof(Request)) || t.IsSubclassOf(typeof(Response)))

                .ToList();

        }

 

        public static IEnumerable<Type> GetKnownTypes(ICustomAttributeProvider provider)

        {

            return knownTypes;

        }

    }

Our real service now looks like this:

    [ServiceContract]

    public interface IProductsService : IService

    {

        [OperationContract]

        GetProductCategoriesResponse GetProductCategories(GetProductCategoriesRequest request);

 

        [OperationContract]

        GetProductOverviewsResponse GetProductOverviews(GetProductOverviewsRequest request);

    }

To handle these requests, i came up with the following RequestHandler type:

    public abstract class RequestHandler : Disposable, IRequestHandler

    {

        public IUnitOfWork UnitOfWork { get; private set; }

 

        protected RequestHandler(IUnitOfWork unitOfWork)

        {

            UnitOfWork = unitOfWork;

        }

 

        protected override void DisposeObjects()

        {

            if (UnitOfWork != null) UnitOfWork.Dispose();

        }

 

        protected override void ClearReferences()

        {

            UnitOfWork = null;

        }

 

        public abstract Response Handle(Request request);

    }

And the implementation of one of these concrete RequestHandlers looks like this:

    public class GetProductOverviewsHandler : RequestHandler

    {

        public ProductRepository ProductRepository { get; private set; }

 

        public GetProductOverviewsHandler(IUnitOfWork unitOfWork, ProductRepository productRepository)

            : base(unitOfWork)

        {

            ProductRepository = productRepository;

        }

 

        public override Response Handle(Request request)

        {

            var typedRequest = (GetProductOverviewsRequest)request;

 

            if (typedRequest.ProductCategoryId.HasValue)

            {

                var fetchStrategy = new FetchStrategy

                    { Association = "Supplier", FetchMode = FetchMode.Join };

                var productOverviews = ProductRepository.FindAllByCategory(

                    typedRequest.ProductCategoryId.Value, fetchStrategy);

                return new GetProductOverviewsResponse(productOverviews.ToOverviewDTOs());

            }

            else

            {

                // TODO: use query batcher to fetch all suppliers and products in one trip

                var productOverviews = ProductRepository.FindAll();

                return new GetProductOverviewsResponse(productOverviews.ToOverviewDTOs());

            }

        }

    }

Now we have to make sure we can actually use this RequestHandlers in a generic manner. I came up with the following base class for my services:

    public abstract class Service : IService

    {

        /// <summary>

        /// server-side service implementation should be stateless, but the IService

        /// interface defines the Dispose method, so we just provide an empty one here

        /// </summary>

        public void Dispose() {}

 

        public Response[] Process(params Request[] requests)

        {

            if (requests == null)

            {

                return null;

            }

 

            var responses = new List<Response>(requests.Length);

 

            foreach (var request in requests)

            {

                Type handlerType = GetHandlerTypeFor(request);

 

                if (handlerType == null)

                {

                    responses.Add(new UnsupportedRequestResponse());

                    continue;

                }

 

                using (var handler = GetHandlerInstance(handlerType))

                {

                    responses.Add(handler.Handle(request));

                }

            }

 

            return responses.ToArray();

        }

 

        public virtual IRequestHandler GetHandlerInstance(Type handlerType)

        {

            return (IRequestHandler)Container.Resolve(handlerType);

        }

 

        public abstract Type GetHandlerTypeFor(Request request);

    }

And the real service then looks like this:

    public class ProductsService : Service, IProductsService

    {

        private static readonly object monitor = new object();

 

        private static readonly Dictionary<Type, Type> requestTypesToRequestHandlerTypes

            = new Dictionary<Type, Type>

            {

                { typeof(GetProductCategoriesRequest), typeof(GetProductCategoriesHandler) },

                { typeof(GetProductOverviewsRequest), typeof(GetProductOverviewsHandler) }

            };

 

        public GetProductCategoriesResponse GetProductCategories(GetProductCategoriesRequest request)

        {

            return (GetProductCategoriesResponse)Process(request)[0];

        }

 

        public GetProductOverviewsResponse GetProductOverviews(GetProductOverviewsRequest request)

        {

            return (GetProductOverviewsResponse)Process(request)[0];

        }

 

        public override Type GetHandlerTypeFor(Request request)

        {

            Type requestType = request.GetType();

 

            lock (monitor)

            {

                if (!requestTypesToRequestHandlerTypes.ContainsKey(requestType))

                {

                    return null;

                }

 

                return requestTypesToRequestHandlerTypes[requestType];

            }

        }

    }

At this point, using a dictionary to map the Request types to the RequestHandler types seems silly, but this is just a small part of the service, more methods will be added so more Request and RequestHandlers will be handled, and in that case i prefer to use a dictionary lookup over a big if statement :)

So now i can do this client-side:

            var results = service.Process(new GetProductCategoriesRequest(), new GetProductOverviewsRequest());

            View.ProductCategories = ((GetProductCategoriesResponse)results[0]).ProductCategories;

            View.Products = ((GetProductOverviewsResponse)results[1]).Products;

And it only uses one remote call which will be much better for performance. At this point, the syntax is not so nice, but i’ll probably provide something similar to my query batcher class so that should improve readability.

So that’s pretty much it… it takes some effort to set this up, and there’s still more to be done (dealing with exceptions in one of the requests for instance) but once you’ve got it set up, i kinda like it. Sure, implementing service methods takes more code than it used to because of the extra classes involved, but they do offer a few very important benefits. You may have noticed that the RequestHandler types receive all of their dependencies through the constructor, and that they are created through the IoC container. This means i can very easily add/remove/change dependencies in my handlers without having to modify code in other places. It also means i can test my service implementations in a thorough manner while providing mocked dependencies instead of the real ones. Another big advantage is that i can add optional parameters to my Request types and thus, offer more functionality through my RequestHandlers without having to update the services and the proxies.

So all in all, while this approach requires to you write more code at first, i think it might actually end up giving you more flexibility for changes later on, while offering you much more control over certain performance characteristics.

16 Responses to “Batching WCF calls”

  1. Jan Van Ryswyck Says:

    Great approach!

  2. The Inquisitive Coder » Blog Archive » The Service Call Batcher Says:

    [...] Batching WCF calls [...]

  3. The Inquisitive Coder » Blog Archive » Batching WCF calls, Take 2 Says:

    [...] Batching WCF calls [...]

  4. Arnon Rotem-Gal-Oz Says:

    This is treating the symptoms instead of the disease. What you should really do is think consider the fallacies of distributed computing and structure your API and tiers accordingly.

    Arnon

  5. Davy Brion Says:

    i think it depends… i’m all in favor of a course-grained service API, but then it’s often difficult to not have the service API be influenced by the client’s requirements. Once that happens, your service API is somewhat coupled to the client, thus making it less interesting/optimal to use for other possible clients.

  6. Udi Dahan Says:

    Just curious, have you taken a look at nServiceBus?

  7. Davy Brion Says:

    I’ve only seen introductions to it, and while i like it, it’s not always an easy sell to customers… WCF (unfortunately) is

  8. Udi Dahan Says:

    I’m hoping to get someone who really understands the nitty gritty internals of WCF to write a transport on top of it for nServiceBus.

    That way you get the best of both worlds :)

  9. Davy Brion Says:

    someone who really understands the nitty gritty internals of WCF? i wonder if the original designers actually understand those :)

  10. The Inquisitive Coder » Blog Archive » Can someone tell me why i shouldn’t drop WCF? Says:

    [...] Batching WCF calls [...]

  11. The Inquisitive Coder - Davy Brion’s Blog » Blog Archive » The Request/Response Service Layer Says:

    [...] point of view. I eventually ended up with an approach that i’ve already documented here and here. Read those posts first before continuing with this post because the rest of this post [...]

  12. TDD with asp.net MVP « My Technical Writings Says:

    [...] http://davybrion.com/blog/2008/06/batching-wcf-calls/ [...]

  13. Tuna Toksoz Says:

    With some IoC magic, I believe you can do it with something like IHandler. Would look better.

    Thanks for the great post.

  14. Davy Brion Says:

    @Tuna

    http://davybrion.com/blog/2008/07/the-request-response-service-layer/

    that post shows a more refined version of this, including the IOC part… it basically resolves the correct handler through the IOC container based on the type of the request

  15. Tuna Toksoz Says:

    That means that I don’t check the dates of the posts! :) Thanks for the pointers

  16. Elegant Code » Tips for ORM Data Access Says:

    [...] requests to your services and database, especially if you are in a web or distributed [...]

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>