Request/Response Service Layer

Introducing: The Agatha Project

4 commentsWritten on November 19th, 2009 by
Categories: agatha, Request/Response Service Layer

As i mentioned earlier in some of the comments, i've received permission from my employer to release our Request/Response Service Layer implementation in a new open source library named Agatha.

If you're interested in trying it out, be sure to download it because i've already made a 1.0 beta 1 release available. Why 1.0 already? Well, because we have been using it in production for over a year now, so it's definitely ready for serious usage already. The only thing that kept me from releasing an official 1.0 version already is that i want to add some small examples in the project: a service layer with 3 different clients (synchronous, asynchronous through Silverlight, and asynchronous through a regular .NET application). The example will be very small, basically just a hello world example but it should be enough to get you going very quickly.

The other thing that is still missing is an abstraction of the logging framework that is used. It's currently still using log4net directly but that's going to change soon. The IOC container usage has already been changed so it doesn't directly depend on Castle Windsor anymore, and it should be very easy to plug in your preferred IOC container. Configuration of the library should be very easy as well, and if you look at the source code you'll notice pretty quickly how easy it can be done. I do plan to make it even easier to configure the thing though. Unfortunately, you still need your typical WCF configuration although i'm going to try to reduce that as much as possible in a future version.

Also, if you look at the source code you'll notice that there are no tests included. I do have tests for it, but i've chosen not to include them in this library since i really want to cover most of it with QuickNet. Regular unit tests will only be written for stuff that really doesn't benefit from using QuickNet.

You're obviously more than welcome to contribute to this project, though i hope you understand that i won't just give anyone commit access right away (apart from the 2 people that already have access at this point) and that you sort of need to earn it. I will happily accept and apply patches, as long as they don't break backwards compatibility, which is obviously pretty important considering the fact that we have quite a few projects that already depend on this. You're obviously also welcome to join the mailinglist where both the usage and the further development of the library can be discussed.

Request/Response Service Layer: Conclusions

18 commentsWritten on November 16th, 2009 by
Categories: Request/Response Service Layer

Note: This post is part of a series. Be sure to read the introduction here.

Now that you've learned everything there is to know about the Request/Response Service Layer (RRSL), i'd like to repeat the benefits of the RRSL that i stated in a previous post:

  • Since we only have one service contract with one service operation, we don't need to spend time thinking about how to design and implement our service contracts and our operations. After all, every operation that the service layer must support is a specific request type that can be added, together with its requesthandler.
  • We can keep our operations as fine-grained as we want (which increases reusability and overall flexibility), without having to pay the cost for chatty network communication by batching multiple requests per roundtrip as much as possible in a transparent manner.
  • The actual implementation of our service is very minimal. It's just a small class which resolves the appropriate requesthandler through the IOC container, based on the type of the incoming request. It then delegates to the requesthandler by passing the request to it, and it returns the response to the client. We can simply add 'operations' by adding request types and requesthandlers to our assemblies... everything gets registered automatically when the application starts up
  • We avoid repetitive code for cross cutting concerns by putting it in a base requesthandler class that the other ones inherit from. That kind of code now only occurs once, and we can plug in custom code at any point of the execution by simply using the Template Method pattern.
  • The implementation of our requesthandlers doesn't contain any code that doesn't have to be there. Each requesthandler simply implements the Handle method to handle the incoming request, and can do as it pleases to fulfill the request. All dependencies are injected automatically by the IOC container. It's usually nothing more than using the dependencies to execute the necessary business logic and then returning a response-derived object.
  • Since we only have one service, we only need one client proxy which never needs to be updated (technically, we have 2: one which is entirely asynch and mostly used in Silverlight clients, and one which is strictly synchronous and is mostly used by ASP.NET applications and Windows Services or command line tools.
  • This single client proxy implementation can make sure that underlying WCF resources are utilized as efficiently as possible and cleaned up properly throughout the client application(s).
  • The client proxy is easy to stub during unit tests which increases the testability of our client side code.
  • Very little configuration. We only have to configure one client-side endpoint, and one server-side endpoint. That's it.
  • All of this is very easy to put in some kind of reusable library. Our applications simply reference the library, inherit from the base requesthandler types, make sure everything is registered properly upon application startup, add a couple of lines of XML and we can start the development of our service layer without any friction.

One of the goals of this series was to convince people that those benefits are indeed real. I really wanted to prove each and every one of those listed benefits and i hope that i succeeded at that. I also hope you realized how cleanly we can write our client code, as well as our actual service layer logic. I definitely recommend that you consider this entire approach for your future applications. While i won't claim that it is the best solution for every project, it's definitely one that can give you quite a few tremendous benefits.

Since i really don't have much more to say about this entire topic, i'm just going to leave it at that :)

Request/Response Service Layer: Testing Asynchronous Client-Side Usage

1 Comment »Written on November 16th, 2009 by
Categories: Request/Response Service Layer

Note: This post is part of a series. Be sure to read the introduction here.

Writing automated tests for asynchronous operations in general can be pretty cumbersome. In the case of the Request/Response Service Layer (RRSL), we basically need to be able to verify that our client code is sending the correct requests, and see how it deals with prepared responses that we send back to the client code through the provided callback. This actually makes it very easy to test the asynchronous usage of the RRSL. We basically just need a different implementation of the IAsyncRequestDispatcher interface, which stores the added requests so we can inspect them later on, and which simply holds a reference to the ResponseReceiver and gives us a specific way to trigger the execution of the ResponseReceiver's logic to call the correct callback from the client code. I'll show the AsyncRequestDispatcherStub class later on, but first we'll take a look at its interface to see which extra methods it provides:

    public interface IAsyncRequestDispatcherStub : IAsyncRequestDispatcher
    {
        void SetResponsesToReturn(params Response[] responses);
        void AddResponseToReturn(Response response, string key);
        bool HasRequest<TRequest>() where TRequest : Request;
        bool HasRequest<TRequest>(string key) where TRequest : Request;
        TRequest GetRequest<TRequest>() where TRequest : Request;
        TRequest GetRequest<TRequest>(string key) where TRequest : Request;
        void ClearRequests();
        void ReturnResponses();
    }

Note that this interface doesn't really exist... it's just shown here to give you a clear view on what specific testing-related functionality the AsyncRequestDispatcherStub offers on top of the regular AsyncRequestDispatcher.

As you can see, we have two methods to add some prepared responses which will be returned to the client code once we call the ReturnResponses method in our test. We also have some methods to inspect the requests that were added by the client code.

And here's the actual code of the AsyncRequestDispatcherStub class:

    public class AsyncRequestDispatcherStub : Disposable, IAsyncRequestDispatcher
    {
        private readonly Dictionary<Type, string> unkeyedTypesToAutoKey;
        private readonly Dictionary<string, Request> requests;
        private readonly Dictionary<string, int> responseKeyToIndexPosition;
        private readonly List<Response> responsesToReturn;
        private ResponseReceiver responseReceiver;
 
        public AsyncRequestDispatcherStub()
        {
            unkeyedTypesToAutoKey = new Dictionary<Type, string>();
            requests = new Dictionary<string, Request>();
            responseKeyToIndexPosition = new Dictionary<string, int>();
            responsesToReturn = new List<Response>();
        }
 
        public void SetResponsesToReturn(params Response[] responses)
        {
            responsesToReturn.Clear();
            responsesToReturn.AddRange(responses);
        }
 
        public void AddResponseToReturn(Response response, string key)
        {
            responsesToReturn.Add(response);
            responseKeyToIndexPosition.Add(key, responsesToReturn.Count - 1);
        }
 
        public bool HasRequest<TRequest>() where TRequest : Request
        {
            return unkeyedTypesToAutoKey.ContainsKey(typeof(TRequest));
        }
 
        public bool HasRequest<TRequest>(string key) where TRequest : Request
        {
            return requests.ContainsKey(key) && (requests[key] is TRequest);
        }
 
        public TRequest GetRequest<TRequest>() where TRequest : Request
        {
            var autoKey = unkeyedTypesToAutoKey[typeof(TRequest)];
            return (TRequest)requests[autoKey];
        }
 
        public TRequest GetRequest<TRequest>(string key) where TRequest : Request
        {
            return (TRequest)requests[key];
        }
 
        public void ClearRequests()
        {
            unkeyedTypesToAutoKey.Clear();
            requests.Clear();
        }
 
        public void Add(Request request)
        {
            var autoKey = Guid.NewGuid().ToString();
            unkeyedTypesToAutoKey.Add(request.GetType(), autoKey);
            requests.Add(autoKey, request);
        }
 
        public void Add(params Request[] requestsToAdd)
        {
            if (requestsToAdd != null)
            {
                foreach (var request in requestsToAdd)
                {
                    Add(request);
                }
            }
        }
 
        public void Add(string key, Request request)
        {
            requests.Add(key, request);
        }
 
        public void ProcessRequests(Action<ReceivedResponses> receivedResponsesDelegate, Action<ExceptionInfo> exceptionOccurredDelegate)
        {
            ProcessRequests(new ResponseReceiver(receivedResponsesDelegate, exceptionOccurredDelegate, responseKeyToIndexPosition));
        }
 
        public void ProcessRequests(Action<ReceivedResponses> receivedResponsesDelegate, Action<ExceptionInfo, ExceptionType> exceptionAndTypeOccurredDelegate)
        {
            ProcessRequests(new ResponseReceiver(receivedResponsesDelegate, exceptionAndTypeOccurredDelegate, responseKeyToIndexPosition));
        }
 
        private void ProcessRequests(ResponseReceiver responseReceiver)
        {
            this.responseReceiver = responseReceiver;
        }
 
        public void ReturnResponses()
        {
            responseReceiver.ReceiveResponses(new ProcessRequestsAsyncCompletedArgs(new[] { responsesToReturn.ToArray() }, null, false, null));
        }
 
        public void Clear()
        {
            // has to be an empty implementation to be able to inspect the added requests
        }
 
        protected override void DisposeManagedResources()
        {
        }
    }

All of this is (once again) very straightforward and we can now very easily verify that our client code is using the RRSL correctly.

Since our client code always receives an IAsyncRequestDispatcher instance through an IAsyncRequestDispatcherFactory, we'll need a different implementation of that factory to be used during our tests:

    public class AsyncRequestDispatcherFactoryStub : IAsyncRequestDispatcherFactory
    {
        private readonly AsyncRequestDispatcherStub asyncRequestDispatcherStub;
 
        public AsyncRequestDispatcherFactoryStub(AsyncRequestDispatcherStub asyncRequestDispatcherStub)
        {
            this.asyncRequestDispatcherStub = asyncRequestDispatcherStub;
        }
 
        public IAsyncRequestDispatcher CreateAsyncRequestDispatcher()
        {
            return asyncRequestDispatcherStub;
        }
    }

And that's all folks.

Request/Response Service Layer: Asynchronous Client-Side Usage

4 commentsWritten on November 15th, 2009 by
Categories: Request/Response Service Layer

Note: This post is part of a series. Be sure to read the introduction here.

First of all, i would like to mention that i am by no means an expert on WCF and asynchronous operations, so it is quite possible that some of the things in this post could be done easier by someone who knows more about it. Most of the code in this post wasn't written by me either, but by my co-worker Tom Ceulemans (who unfortunately doesn't have a blog that i can link to). What you'll see in this post does work and it actually works very well. But as i said, there very well might be room for some nice improvements here. Anyways, let's get to it.

As you know by now, our Service Contract for the Request/Response Service Layer (RRSL) looks like this:

    [ServiceContract]
    public interface IWcfRequestProcessor
    {
        [OperationContract(Name = "ProcessRequests")]
        [ServiceKnownType("GetKnownTypes", typeof(KnownTypeProvider))]
        Response[] Process(params Request[] requests);
    }

As you can see, this Service Contract doesn't define any asynchronous operations. We don't need to define them in the original contract, but we do have to use an asynchronous version of this Service Contract in the client from which we want to make asynchronous calls to our RRSL. So client-side, we use the following version of the IWcfRequestProcessor Service Contract:

    [ServiceContract(ConfigurationName = "Namespace.Of.Your.IWcfRequestProcessor")]
    public interface IWcfRequestProcessor : IDisposable
    {
        [OperationContract(AsyncPattern = true, Name = "ProcessRequests")]
        [ServiceKnownType("GetAllKnownTypes", typeof(KnownTypeProvider))]
        IAsyncResult BeginProcessRequests(Request[] requests, AsyncCallback callback, object asyncState);
 
        Response[] EndProcessRequests(IAsyncResult result);
 
        void ProcessRequestsAsync(Request[] requests, Action<ProcessRequestsAsyncCompletedArgs> processCompleted);
    }

This one only defines the asynchronous version of the ProcessRequests operation, which in our case is all we need since we use this in our Silverlight applications where you never make synchronous remote calls.

The ProcessRequestsAsyncCompletedArgs class looks like this:

    public class ProcessRequestsAsyncCompletedArgs : System.ComponentModel.AsyncCompletedEventArgs
    {
        private readonly object[] results;
 
        public ProcessRequestsAsyncCompletedArgs(object[] results, Exception exception, bool cancelled, object userState) :
            base(exception, cancelled, userState)
        {
            this.results = results;
        }
 
        public Response[] Result
        {
            get
            {
                RaiseExceptionIfNecessary();
                return ((Response[])(results[0]));
            }
        }
    }

Now we need a proxy class to implement the asynchronous version of the IWcfRequestProcessor interface. The code of the AsyncWcfRequestProcessorProxy code is somewhat low-level because it's (obviously) dealing with all of the async stuff, so there's not really a lot of need to get into the details of this piece of code. If you're implementing your own RRSL, just copy this code and be glad that you didn't have to write it ;)

    public class AsyncWcfRequestProcessorProxy : ClientBase<IWcfRequestProcessor>, IWcfRequestProcessor
    {
        public event EventHandler<AsyncCompletedEventArgs> OpenCompleted;
 
        public AsyncWcfRequestProcessorProxy() {}
 
        public AsyncWcfRequestProcessorProxy(string endpointConfigurationName, string remoteAddress)
            : base(endpointConfigurationName, remoteAddress) {}
 
        IAsyncResult IWcfRequestProcessor.BeginProcessRequests(Request[] requests, AsyncCallback callback, object asyncState)
        {
            return Channel.BeginProcessRequests(requests, callback, asyncState);
        }
 
        [System.Diagnostics.DebuggerHidden]
        Response[] IWcfRequestProcessor.EndProcessRequests(IAsyncResult result)
        {
            return Channel.EndProcessRequests(result);
        }
 
        private IAsyncResult OnBeginProcessRequests(object[] inValues, AsyncCallback callback, object asyncState)
        {
            var requests = ((Request[])(inValues[0]));
            return ((IWcfRequestProcessor)(this)).BeginProcessRequests(requests, callback, asyncState);
        }
 
        [System.Diagnostics.DebuggerHidden]
        private object[] OnEndProcessRequests(IAsyncResult result)
        {
            Response[] retVal = ((IWcfRequestProcessor)(this)).EndProcessRequests(result);
            return new object[] { retVal };
        }
 
        [System.Diagnostics.DebuggerHidden]
        private void OnProcessRequestsCompleted(object state)
        {
            var e = ((InvokeAsyncCompletedEventArgs)(state));
            ((Action<ProcessRequestsAsyncCompletedArgs>)(e.UserState)).Invoke(new ProcessRequestsAsyncCompletedArgs(e.Results, e.Error, e.Cancelled, e.UserState));
        }
 
        public void ProcessRequestsAsync(Request[] requests, Action<ProcessRequestsAsyncCompletedArgs> processCompleted)
        {
            InvokeAsync(OnBeginProcessRequests, new object[] { requests },
                        OnEndProcessRequests, OnProcessRequestsCompleted, processCompleted);
        }
 
        private IAsyncResult OnBeginOpen(object[] inValues, AsyncCallback callback, object asyncState)
        {
            return ((ICommunicationObject)(this)).BeginOpen(callback, asyncState);
        }
 
        private object[] OnEndOpen(IAsyncResult result)
        {
            ((ICommunicationObject)(this)).EndOpen(result);
            return null;
        }
 
        private void OnOpenCompleted(object state)
        {
            if ((OpenCompleted != null))
            {
                var e = ((InvokeAsyncCompletedEventArgs)(state));
                OpenCompleted(this, new AsyncCompletedEventArgs(e.Error, e.Cancelled, e.UserState));
            }
        }
 
        public void OpenAsync()
        {
            OpenAsync(null);
        }
 
        public void OpenAsync(object userState)
        {
            InvokeAsync(OnBeginOpen, null, OnEndOpen, OnOpenCompleted, userState);
        }
 
        private IAsyncResult OnBeginClose(object[] inValues, AsyncCallback callback, object asyncState)
        {
            return ((ICommunicationObject)(this)).BeginClose(callback, asyncState);
        }
 
        private object[] OnEndClose(IAsyncResult result)
        {
            ((ICommunicationObject)(this)).EndClose(result);
            return null;
        }
 
        private void OnCloseCompleted(object state)
        {
            var e = ((InvokeAsyncCompletedEventArgs)(state));
            CloseCompleted(new AsyncCompletedEventArgs(e.Error, e.Cancelled, e.UserState));
        }
 
        public void CloseCompleted(AsyncCompletedEventArgs args)
        {
            if (args.Error != null)
            {
                Abort();
            }
        }
 
        public void CloseAsync()
        {
            CloseAsync(null);
        }
 
        public void CloseAsync(object userState)
        {
            InvokeAsync(OnBeginClose, null, OnEndClose, OnCloseCompleted, userState);
        }
 
        protected override IWcfRequestProcessor CreateChannel()
        {
            return new WcfRequestProcessorClientChannel(this);
        }
 
        private class WcfRequestProcessorClientChannel : ChannelBase<IWcfRequestProcessor>, IWcfRequestProcessor
        {
            public WcfRequestProcessorClientChannel(ClientBase<IWcfRequestProcessor> client) :
                base(client)
            {
            }
 
            public IAsyncResult BeginProcessRequests(Request[] requests, AsyncCallback callback, object asyncState)
            {
                var _args = new object[1];
                _args[0] = requests;
                IAsyncResult _result = BeginInvoke("ProcessRequests", _args, callback, asyncState);
                return _result;
            }
 
            [System.Diagnostics.DebuggerHidden]
            public Response[] EndProcessRequests(IAsyncResult result)
            {
                var _args = new object[0];
                var _result = ((Response[])(EndInvoke("ProcessRequests", _args, result)));
 
                result.AsyncWaitHandle.Close();
 
                return _result;
            }
 
            public void ProcessRequestsAsync(Request[] requests, Action<ProcessRequestsAsyncCompletedArgs> processCompleted)
            {
                throw new NotImplementedException();
            }
        }
 
        public void Dispose()
        {
            CloseAsync();
        }
    }

All you would need to be able to make asynchronous calls to the RRSL right now, is the following client-side WCF configuration:

  <system.serviceModel>
    <bindings>
      <basicHttpBinding>
        <binding name="BasicHttpBinding_IWcfRequestProcessor_HTTP" maxBufferSize="2147483647"
          maxReceivedMessageSize="2147483647">
          <security mode="None"/>
        </binding>
      </basicHttpBinding>
    </bindings>
    <client>
      <endpoint binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IWcfRequestProcessor_HTTP"
        contract="Namespace.Of.Your.IWcfRequestProcessor" name="BasicHttpBinding_IWcfRequestProcessor_HTTP" />
    </client>
  </system.serviceModel>

Note, this is an example taken from a Silverlight client... in case of a regular .NET client the WCF configuration will be slightly bigger (and pretty much similar to the one in the example of synchronous RRSL usage).

That's all you really need to be able to use the RRSL asynchronously from a client. Of course, using the AsyncWcfRequestProcessorProxy class would be even more clumsy and error prone than using the WcfRequestProcessorProxy (from the synchronous usage post) class directly. Ideally, we should be able to use something similar to the IRequestDispatcher, only asynchronously. And thus, the IAsyncRequestDispatcher interface was born:

    public interface IAsyncRequestDispatcher : IDisposable
    {
        void Add(Request request);
        void Add(params Request[] requestsToAdd);
        void Add(string key, Request request);
        void ProcessRequests(Action<ReceivedResponses> receivedResponsesDelegate, Action<ExceptionInfo> exceptionOccurredDelegate);
        void ProcessRequests(Action<ReceivedResponses> receivedResponsesDelegate, Action<ExceptionInfo, ExceptionType> exceptionAndTypeOccurredDelegate);
    }

Its usage is pretty similar to that of the IRequestDispatcher, except that you don't access the received responses directly. Instead, you tell the IAsyncRequestDispatcher to process the requests and you can provide some callbacks. The first callback needs to be a method which accepts a ReceivedResponses instance as a parameter (we'll get to that class later on in the post). The second callback is a method which either receives an ExceptionInfo object as a parameter, or both an ExceptionInfo and ExceptionType parameter. The last callback will obviously only be called if something went wrong.

Another big difference between the IAsyncRequestDispatcher and the IRequestDispatcher is that the IAsyncRequestDispatcher is not ment to be reused for multiple service calls. That is, you can obviously add as many requests as you like but you can only call the ProcessRequests method once, at which point all of the added requests will be sent to the RRSL through the AsyncWcfRequestProcessorProxy class. The reason why we chose to go the "You can only use it once"-route is to guarantee that the IAsyncRequestDispatcher and especially its AsyncWcfRequestProcessorProxy instance are always guaranteed to be disposed properly no matter when the responses are returned, which might be after the view-component has already been closed by the user, for instance.

Now, our implementation of the IAsyncRequestDispatcher interface is dependent upon 3 other classes that we wrote. The first is the AsyncWcfRequestProcessoryProxy class which we already covered. The other two are the ReceivedResponses class, and the ResponseReceiver class. I'll show the implementations of those classes after i show the code of the AsyncRequestDispatcher so you might have to scroll back and forth between the code of these classes in order to grasp the code.

First of all, the AsyncRequestDispatcher:

    public class AsyncRequestDispatcher : Disposable, IAsyncRequestDispatcher
    {
        private readonly IWcfRequestProcessor requestProcessor;
        protected Dictionary<string, int> keyToResultPositions;
        private Dictionary<string, Type> keyToTypes;
 
        private List<Request> queuedRequests;
 
        public AsyncRequestDispatcher(IWcfRequestProcessor requestProcessor)
        {
            this.requestProcessor = requestProcessor;
            InitializeState();
        }
 
        public virtual Request[] QueuedRequests
        {
            get { return queuedRequests.ToArray(); }
        }
 
        public virtual void Add(params Request[] requestsToAdd)
        {
            foreach (var request in requestsToAdd)
            {
                Add(request);
            }
        }
 
        public virtual void Add(string key, Request request)
        {
            AddRequest(request, true);
            keyToTypes[key] = request.GetType();
            keyToResultPositions[key] = queuedRequests.Count - 1;
        }
 
        public virtual void Add(Request request)
        {
            AddRequest(request, false);
        }
 
        public virtual void ProcessRequests(Action<ReceivedResponses> receivedResponsesDelegate, Action<ExceptionInfo> exceptionOccurredDelegate)
        {
            ProcessRequests(new ResponseReceiver(receivedResponsesDelegate, exceptionOccurredDelegate, keyToResultPositions));
        }
 
        public virtual void ProcessRequests(Action<ReceivedResponses> receivedResponsesDelegate, Action<ExceptionInfo, ExceptionType> exceptionAndTypeOccurredDelegate)
        {
            ProcessRequests(new ResponseReceiver(receivedResponsesDelegate, exceptionAndTypeOccurredDelegate, keyToResultPositions));
        }
 
        private void ProcessRequests(ResponseReceiver responseReciever)
        {
            var requests = queuedRequests.ToArray();
            BeforeSendingRequests(requests);
            requestProcessor.ProcessRequestsAsync(requests, a => OnProcessRequestsCompleted(a, responseReciever));
        }
 
        protected virtual void BeforeSendingRequests(IEnumerable<Request> requestsToProcess) { }
 
        public virtual void OnProcessRequestsCompleted(ProcessRequestsAsyncCompletedArgs args, ResponseReceiver responseReciever)
        {
            Dispose();
            responseReciever.ReceiveResponses(args);
        }
 
        protected override void DisposeManagedResources()
        {
            if (requestProcessor != null) requestProcessor.Dispose();
        }
 
        private void AddRequest(Request request, bool wasAddedWithKey)
        {
            Type requestType = request.GetType();
 
            if (RequestTypeIsAlreadyPresent(requestType) &&
                (RequestTypeIsNotAssociatedWithKey(requestType) || !wasAddedWithKey))
            {
                throw new InvalidOperationException(String.Format("A request of type {0} has already been added. "
                                                                  + "Please add requests of the same type with a different key.", requestType.FullName));
            }
 
            queuedRequests.Add(request);
        }
 
        private bool RequestTypeIsAlreadyPresent(Type requestType)
        {
            return QueuedRequests.Count(r => r.GetType().Equals(requestType)) > 0;
        }
 
        private bool RequestTypeIsNotAssociatedWithKey(Type requestType)
        {
            return !keyToTypes.Values.Contains(requestType);
        }
 
        private void InitializeState()
        {
            queuedRequests = new List<Request>();
            keyToTypes = new Dictionary<string, Type>();
            keyToResultPositions = new Dictionary<string, int>();
        }
    }

There's nothing complex or difficult about this class. You can basically add requests just as you could do with the synchronous RequestDispatcher, and when you call the ProcessRequests method, we create a ResponseReceiver which will also be passed into the method that will be called once the responses have returned from the asynchronous proxy. When those responses are returned, we dispose our own instance of the AsyncRequestDispatcher (which in turn disposes the AsyncWcfRequestProcessorProxy) and then we ask the ResponseReceiver to handle the received responses. Nothing complicated, but you might have to take a second look if you didn't get it the first time (and you certainly wouldn't be the first).

The implementation of the ResponseReceiver class looks like this:

    public class ResponseReceiver
    {
        private readonly Action<ReceivedResponses> responseReceivedCallback;
        private readonly Action<ExceptionInfo, ExceptionType> exceptionAndTypeOccuredCallback;
        private readonly Action<ExceptionInfo> exceptionOccurredCallback;
        private readonly Dictionary<string, int> keyToResultPositions;
 
        public ResponseReceiver(Action<ReceivedResponses> responseReceivedCallback, Action<ExceptionInfo> exceptionOccurredCallback,
            Dictionary<string, int> keyToResultPositions)
        {
            if (responseReceivedCallback == null) throw new ArgumentNullException("responseReceivedCallback");
            if (exceptionOccurredCallback == null) throw new ArgumentNullException("exceptionOccurredCallback");
 
            this.responseReceivedCallback = responseReceivedCallback;
            this.exceptionOccurredCallback = exceptionOccurredCallback;
            this.keyToResultPositions = keyToResultPositions;
        }
 
        public ResponseReceiver(Action<ReceivedResponses> responseReceivedCallback, Action<ExceptionInfo, ExceptionType> exceptionAndTypeOccuredCallback,
            Dictionary<string, int> keyToResultPositions)
        {
            if (responseReceivedCallback == null) throw new ArgumentNullException("responseReceivedCallback");
            if (exceptionAndTypeOccuredCallback == null) throw new ArgumentNullException("exceptionAndTypeOccuredCallback");
 
            this.responseReceivedCallback = responseReceivedCallback;
            this.exceptionAndTypeOccuredCallback = exceptionAndTypeOccuredCallback;
            this.keyToResultPositions = keyToResultPositions;
        }
 
        public void ReceiveResponses(ProcessRequestsAsyncCompletedArgs args)
        {
            if (HasException(args))
            {
                HandleException(args);
            }
            else
            {
                var disposable = responseReceivedCallback.Target as Disposable;
 
                if (disposable == null || !disposable.IsDisposed)
                {
                    responseReceivedCallback(new ReceivedResponses(args.Result, keyToResultPositions));
                }
            }
        }
 
        private void HandleException(ProcessRequestsAsyncCompletedArgs args)
        {
            var disposable = responseReceivedCallback.Target as Disposable;
 
            if (disposable == null || !disposable.IsDisposed)
            {
                var exception = GetException(args);
 
                if (exceptionOccurredCallback != null)
                {
                    exceptionOccurredCallback(exception);
                }
                else if (exceptionAndTypeOccuredCallback != null)
                {
                    var exceptionType = GetExceptionType(args);
 
                    exceptionAndTypeOccuredCallback(exception, exceptionType);
                }
                else
                {
                    responseReceivedCallback(new ReceivedResponses(args.Result, keyToResultPositions));
                }
            }
        }
 
        private static bool HasException(ProcessRequestsAsyncCompletedArgs args)
        {
            if (args.Error == null)
            {
                return args.Result.Any(r => r.Exception != null);
            }
 
            return true;
        }
 
        private static ExceptionInfo GetException(ProcessRequestsAsyncCompletedArgs args)
        {
            if (args.Error == null)
            {
                var responseWithException = GetFirstException(args.Result);
                if (responseWithException != null)
                {
                    return responseWithException.Exception;
                }
 
                return null;
            }
 
            return new ExceptionInfo(args.Error);
        }
 
        private static ExceptionType GetExceptionType(ProcessRequestsAsyncCompletedArgs args)
        {
            if (args.Error == null)
            {
                var responseWithException = GetFirstException(args.Result);
 
                if (responseWithException != null)
                {
                    return responseWithException.ExceptionType;
                }
            }
 
            return ExceptionType.Unknown;
        }
 
        private static Response GetFirstException(IEnumerable<Response> responsesToCheck)
        {
            return responsesToCheck.FirstOrDefault(r => r.Exception != null);
        }
    }

Pretty straightforward... it basically makes sure that either the callback from the original caller is called to handle the received responses, or that the callback is called to deal with exceptions. The callback to handle the received responses receives a ReceivedResponses instance, which again makes it possible to easily retrieve the response you need:

    public class ReceivedResponses
    {
        private readonly Response[] responses;
        private readonly Dictionary<string, int> keyToResultPositions;
 
        public ReceivedResponses(Response[] responses)
            : this(responses, new Dictionary<string, int>()) {}
 
        public ReceivedResponses(Response[] responses, Dictionary<string, int> keyToResultPositions)
        {
            this.responses = responses;
            this.keyToResultPositions = keyToResultPositions;
        }
 
        public virtual TResponse Get<TResponse>() where TResponse : Response
        {
            var responseType = typeof(TResponse);
            return (TResponse)responses.Single(r => r.GetType().Equals(responseType));
        }
 
        public virtual TResponse Get<TResponse>(string key) where TResponse : Response
        {
            return (TResponse)responses[keyToResultPositions[key]];
        }
 
        public virtual bool HasResponse<TResponse>() where TResponse : Response
        {
            return responses.OfType<TResponse>().Any();
        }
    }

Now, some of you will probably be thinking "isn't all this more complex than it needs to be?". Apart from the asynchronous proxy, i truly doubt it. The only thing that your code needs to know of is the API of the IAsyncRequestDispatcher interface and of the ReceivedResponses class which are both pretty clean, very easy to use and easy to grasp.

One final word about the fact that the IAsyncRequestDispatcher is only meant to be used once. Obviously, we don't create each IAsyncRequestDispatcher instance manually. We can't have the IOC container inject it whenever we want either, because then we'd only have one instance for the lifetime of the class that had the IAsyncRequestDispatcher injected. We inject the following factory instead:

    public interface IAsyncRequestDispatcherFactory
    {
        IAsyncRequestDispatcher CreateAsyncRequestDispatcher();
    }

The implementation of which looks like this:

    public class AsyncRequestDispatcherFactory : IAsyncRequestDispatcherFactory
    {
        public IAsyncRequestDispatcher CreateAsyncRequestDispatcher()
        {
            return IoC.Container.Resolve<IAsyncRequestDispatcher>();
        }
    }

One thing that you need to be very careful of: if your IAsyncRequestDispatcherFactory implementation happens to use Castle Windsor's container (ours doesn't because we have our own custom little container for our Silverlight apps) then you absolutely have to make sure that the Dispose method of the IAsyncRequestDispatcher implementation calls the Release method of the Windsor container. More information on why you'd need to do that can be found here and here.

That's it for this post, which is probably the most difficult one to comprehend but again, the most important facts to remember are the ease of use of IAsyncRequestDispatcher and ReceivedResponses. Also, keep in mind that even though we use this primarily for Silverlight clients, you can just as well do this from WPF applications or any other .NET application for that matter.

Finally, i'd like to thank Tom Ceulemans for the implementation shown in this post. He happened to be the first one who needed to use the RRSL from a silverlight application and he did a great job with getting it to work :)

Request/Response Service Layer: Testing Synchronous Client-Side Usage

2 commentsWritten on November 13th, 2009 by
Categories: Request/Response Service Layer

Note: This post is part of a series. Be sure to read the introduction here.

If you want to write automated tests for your client-side code, it's often useful to replace the service proxy with a mock instance. With typical WCF services and their proxies, that's pretty easy to do. With the Request/Response Service Layer (RRSL) and its IRequestDispatcher, it's a bit more tricky. While you could provide a mock instance of IRequestDispatcher to your classes under test, we've learned that it's easier to use a prepared stub class which inherits from the RequestDispatcher class and adds some extra methods to inspect the requests that were supposed to be sent, and to return response objects that you can easily prepare yourself.

If we go back to our implementation of the IRequestDispatcher interface, you'll notice that the RequestDispatcher class has the following constructor:

        protected RequestDispatcher(IRequestProcessor requestProcessor)
        {
            this.requestProcessor = requestProcessor;
            InitializeState();
        }

As well as the following virtual method which is the only place where we actually use the IRequestProcessor to send requests to the Request Processor:

        protected virtual Response[] GetResponses(params Request[] requestsToProcess)
        {
            BeforeSendingRequests(requestsToProcess);
            return requestProcessor.Process(requestsToProcess);
        }

You'll also notice that most of the public methods of the RequestDispatcher class are virtual and that many of its protected methods are virtual as well. While we don't need to override all of them, there's plenty of flexibility to do whatever you want to do. We'll basically pass a null reference to the RequestDispatcher's constructor (which takes an IRequestProcessor instance) and override the protected GetResponses method to simply return our prepared responses instead of actually sending them to the IRequestProcessor. We'll also add a few methods so you can add prepared responses in your tests, as well as some methods which allow you to easily inspect whether certain requests were added by the code under test, and to retrieve the actual requests so you can verify that they contain the expected data.

There is no interface to define the added functionality of the stub, but this fictional interface might make it clearer what you'll be able to do with the stub in your tests:

    public interface IRequestDispatcherStub : IRequestDispatcher
    {
        void AddResponsesToReturn(params Response[] responses);
        void AddResponsesToReturn(Dictionary<string, Response> keyedResponses);
        void AddResponseToReturn(Response response);
        void AddResponseToReturn(string key, Response response);
        TRequest GetRequest<TRequest>() where TRequest : Request;
        TRequest GetRequest<TRequest>(string key) where TRequest : Request;
        bool HasRequest<TRequest>() where TRequest : Request;
    }

Those added methods make it very clear to verify that your code under test is communicating with the RRSL in the way you intended it to.

So finally, this is the code of the RequestDispatcherStub class:

    public class RequestDispatcherStub : RequestDispatcher
    {
        private readonly List<Response> responsesToReturn = new List<Response>();
        private readonly Dictionary<string, Request> keyToRequest = new Dictionary<string, Request>();
 
        public RequestDispatcherStub() : base(null) { }
 
        public void AddResponsesToReturn(params Response[] responses)
        {
            responsesToReturn.AddRange(responses);
        }
 
        public void AddResponsesToReturn(Dictionary<string, Response> keyedResponses)
        {
            responsesToReturn.AddRange(keyedResponses.Values);
 
            for (int i = 0; i < keyedResponses.Keys.Count; i++)
            {
                var key = keyedResponses.Keys.ElementAt(i);
 
                if (key != null)
                {
                    keyToResultPositions.Add(key, i);
                }
            }
        }
 
        public void AddResponseToReturn(Response response)
        {
            responsesToReturn.Add(response);
        }
 
        public void AddResponseToReturn(string key, Response response)
        {
            responsesToReturn.Add(response);
            keyToResultPositions.Add(key, responsesToReturn.Count - 1);
        }
 
        public override void Clear()
        {
            // this Stub can't clear the state because we have to be able to inspect the sent requests
            // during our tests
        }
 
        public override void Add(string key, Request request)
        {
            base.Add(key, request);
            keyToRequest[key] = request;
        }
 
        public TRequest GetRequest<TRequest>() where TRequest : Request
        {
            return (TRequest)SentRequests.First(r => r.GetType().Equals(typeof(TRequest)));
        }
 
        public TRequest GetRequest<TRequest>(string key) where TRequest : Request
        {
            return (TRequest)keyToRequest[key];
        }
 
        public bool HasRequest<TRequest>() where TRequest : Request
        {
            return SentRequests.Count(r => r.GetType().Equals(typeof(TRequest))) > 0;
        }
 
        protected override Response[] GetResponses(params Request[] requestsToProcess)
        {
            return responsesToReturn.ToArray();
        }
 
        protected override void DealWithSecurityException(ExceptionInfo exceptionInfo)
        {
            throw new SecurityException("a security exception was thrown: " + exceptionInfo);
        }
 
        protected override void DealWithUnknownException(ExceptionInfo exceptionInfo)
        {
            throw new Exception("an unknown exception was thrown: " + exceptionInfo);
        }
    }

With this in place, you can very easily test whether the correct requests have been sent, whether they contain the expected data, and how your code reacts to the data in your prepared responses.