Thursday, February 23, 2012

Mock Asynchronous Web Services - Part 2

 

Following on from part 1, I will show how to mock out an synchronous service that has the BeginMethod and EndMethod access pattern (the IAsyncResult pattern) rather than the MethodAsync and OnMethodCompleted event variety (the Event based pattern). Also I will show how to introduce a delay to your asynchronous response, something we glossed over last post. This is fairly important as the whole point of using an async service is your app should be doing something else whilst waiting for the response. Eg. in silverlight you might have waiting spinners showing and various commands disabled until the service returns but the rest of the UI should remain responsive.

Before looking at that I just wanted to run through the some advantages of mocking out all your web service dependencies:

Unit testing – If you are unit testing bits of your app (normally the Model and maybe the View Model for silverlight) then it is far easier when you have control over the exact data returned. I’ve written and seen plenty of tests that rely on specific sets of data in a ‘real’ system and it is a pain to setup and rather brittle.

Manual testing of specific conditions – even if you are just manually testing your app there will be various conditions that are quite hard to replicate without a mock service. You can easily simulate various exceptions being thrown by the web service, odd sets of data being returned, timeouts etc… If you are having to setup data in a third party system to cover all these cases then this will take far longer than creating a few mock services will.

Off-line development – It is useful to be able to develop your app without needing various other systems to be up and running. For example the silverlight app I have been doing recently calls Dynamics CRM, Sharepoint and a custom web service, you don’t want all these running on your dev machine.

 

Anyway, on to creating a mock for a service that uses the standard Begin / End async pattern.

Firstly, exactly like in the last post, we pull out the relevant methods from the generated Reference.cs file into an adapter interface (you may recognise this as the signature of the Dynamics CRM service method that takes fetchXml):

    public interface ICrmServiceAdapter

    {

        IAsyncResult BeginRetrieveMultiple(QueryBase query, AsyncCallback callback, object asyncState);

        EntityCollection EndRetrieveMultiple(IAsyncResult result);

    }

 

 

and then implement a ‘pass through’ version for the real service:

 

    public class CrmServiceAdapter : ICrmServiceAdapter

    {

        IOrganizationService service;

        public CrmServiceAdapter(IOrganizationService service)

        {

            this.service = service;

        }

 

        public IAsyncResult BeginRetrieveMultiple(QueryBase query, AsyncCallback callback, object asyncState)

        {

            return service.BeginRetrieveMultiple(query, callback, asyncState);

        }

 

        public EntityCollection EndRetrieveMultiple(IAsyncResult result)

        {

            return service.EndRetrieveMultiple(result);

        }

    }

 

 

Then we need to create a mock implementation of this adapter interface. The key realisation here is that you need to create your own IAsyncResult object to return from the Begin method. This interface defines a few important properties:

 

AsyncState – this will hold the data that lets the EndRetrieveMultiple method know what mock data it should be returning (it could store the mock data itself)

AsyncWaitHandle – this is so consumers can await the completion of the operation

IsCompleted – this should indicate whether the operation has completed.

 

Here is the implementation of this interface we will use:

 

    private class AsyncResult : IAsyncResult

    {

        private WaitHandle waitHandle = new ManualResetEvent(false);

 

        public bool IsCompleted

        {

            get { return waitHandle.WaitOne(0); }

        }

 

        private Dictionary<string, object> state;

 

        public AsyncResult(Dictionary<string, object> state) { this.state = state; }

 

        public object AsyncState { get { return state; } }

 

        public WaitHandle AsyncWaitHandle { get { return waitHandle; } }

 

        public bool CompletedSynchronously { get { return false; } }

    }

 

We use a ManualResetEvent as the waithandle. This will allow us to signal that the operation has completed by calling the Set() method. The IsCompleted property just checks if this has been signalled yet by waiting for it with a timeout of 0.

 

Now our MockAdapter:

 

 

    public class CrmServiceMockAdapter : ICrmServiceAdapter

    {

        private class AsyncResult : IAsyncResult

        {

            /* snip - see above*/

        }

 

        const int maxResponseDelay = 5000;

 

        private void invokeResponse(Action action)

        {

            ThreadPool.QueueUserWorkItem(new WaitCallback( _ =>

                {

                    Thread.Sleep(new Random().Next(maxResponseDelay));

                    action();                   

                }));

        }

 

        public IAsyncResult BeginRetrieveMultiple(Xrm.Silverlight.Sdk.QueryBase query, AsyncCallback callback, object asyncState)

        {

            /* snip - set state according to the input so we know what to return from the End method */

            IAsyncResult asr = new AsyncResult(state);

            Action responseCompleteAction = () =>

            {

                ((ManualResetEvent)asr.AsyncWaitHandle).Set();

                callback(asr);

            };

 

            invokeResponse(responseCompleteAction);

            return asr;

        }

 

        public Xrm.Silverlight.Sdk.EntityCollection EndRetrieveMultiple(IAsyncResult result)

        {

            /* snip - return some mock data according to the contents of result.AsyncState */

        }

    }

  

Note how we queue up a work item on the ThreadPool to do the following (this is the bit you need to add to the code in the last post to simulate a long running service call):

 

1) Delay for a random amount of time

2) Set the ManualResetEvent to indicate completion

3) Call the callback function

 

Note Random is not thread safe so don’t be tempted to make this a member of the class.

 

 

And that pretty much covers it. Finish by creating a factory interface and two implementations exactly like in the last post and you should be up a running with a freshly minted mock CRM service:

var crmService = crmServiceFactory.Create();

crmService.BeginRetrieveMultiple(fetchXml, asr =>

{

var result = crmService.EndRetrieveMultiple(asr);

/*snip - do whatever with your results */

}, null);

 

Are you sure you want to use IAsyncResult?

It’s worth noting that using IAsyncResult async when developing from scratch is only really recommended in the following situations (taken from http://msdn.microsoft.com/en-us/library/ms228966.aspx):

 

  • Only expose the IAsyncResult pattern when you specifically require support for WaitHandle or IAsyncResult objects.

  • Only expose the IAsyncResult pattern when you have an existing API that uses the IAsyncResult pattern.

  • If you have an existing API based on the IAsyncResult pattern, consider also exposing the event-based pattern in your next release.

  • Only expose IAsyncResult pattern if you have high performance requirements which you have verified cannot be met by the event-based pattern but can be met by the IAsyncResult pattern.

 

If you do have an existing service that uses this pattern and you would rather use the Event based pattern then you could easily write an adapter interface to convert from one to the other. This is pretty much what is done for you when you generate an async service reference in .Net 3.5 and above, although for some reason it only adds the event based methods to the Client class rather than the generated interface.

 

Friday, February 17, 2012

Mock Asynchronous Web Services (Silverlight)

 

Adding a Service Reference in a silverlight project will only generate asynchronous service methods. It is fairly well known how create a mock service for a normal service reference, in this post I will run through an easy way to do this for an asynchronous service. This should enable you to test your silverlight apps better (or develop them on the train without wi-fi in my case).

Example Service

Let’s use a really simple service definition which is created when you add a new WCF Service Application project:

    [ServiceContract]

    public interface IService

    {

        [OperationContract]

        string GetData(int value);

    }

    public class Service : IService

    {

        public string GetData(int value)

        {

            return string.Format("You entered: {0}", value);

        }

    }

and add a service reference to this in a silverlight project. If you now look at the Reference.cs generated we see the following IService definition:

 

 

The Code Behind

 

    public interface IService

    {

 

        System.IAsyncResult BeginGetData(int value, System.AsyncCallback callback, object asyncState);

 

        string EndGetData(System.IAsyncResult result);

    }

 

It has generated a BeginGetData and EndGetData pair of methods following the async pattern. The client proxy generated does implement this interface, however these methods are hidden and some new public methods and events are added (I’ve cut out most of the irrelevant stuff to make it clearer):

 

 

    public partial class ServiceClient : ClientBase<IService>, IService

    {

        public ServiceClient(Binding binding, EndpointAddress remoteAddress) :

            base(binding, remoteAddress)

        {

        }

 

        [EditorBrowsableAttribute(EditorBrowsableState.Advanced)]

        string IService.EndGetData(IAsyncResult result) { /*snip*/ }

 

        public void GetDataAsync(int value) { /*snip*/ }

 

        public event EventHandler<GetDataCompletedEventArgs> GetDataCompleted;

 

        [EditorBrowsableAttribute(EditorBrowsableState.Advanced)]

IAsyncResult IService.BeginGetData(int value, AsyncCallback callback, object asyncState) { /*snip*/ }

    }

 

We can see that the actual interface methods have been marked EditorBrowsableState.Advanced meaning you won’t see them in intellisense. The ‘visible’ members, GetDataAsync and GetDataCompleted exist only on the proxy class and it is these you normally use when calling the service in your client, something like this:

            ServiceClient client = new ServiceClient(new BasicHttpBinding(), new EndpointAddress("http://service.com/service.svc"));

            client.GetDataCompleted += new EventHandler<GetDataCompletedEventArgs>

                (

                    (sender,e) => Console.WriteLine(e.Result)

                );

 

            client.GetDataAsync(10);

 

 

It is this method and event that we need to implement in our mock.

 

 

Mocktastic

 

The easiest way I have found to do this is to first pull out the relevant members into an adapter interface:

 

    public interface IServiceAdapter

    {

        void GetDataAsync(int value);

        event System.EventHandler<GetDataCompletedEventArgs> GetDataCompleted;

    }

 

And implement a service adapter that takes an instance of the proxy and passes through the method calls and the event subscriptions by overriding the default add and remove accessors for the event:

 

    public class ServiceAdapter : IServiceAdapter

    {

        ServiceClient client;

 

        public ServiceAdapter(ServiceClient client)

        {

            this.client = client;

        }

 

        public void GetDataAsync(int value)

        {

            client.GetDataAsync(value);

        }

 

        public event EventHandler<GetDataCompletedEventArgs> GetDataCompleted

        {

            add

            {

                client.GetDataCompleted += value;

            }

            remove

            {

                client.GetDataCompleted -= value;

            }

        }

    }

 

 

 

Add another adapter which implements whatever mock functionality you want:

 

    public class MockServiceAdapter : IServiceAdapter

    {

        int mockData;

        public MockServiceAdapter(int mockData)

        {

            this.mockData = mockData;

        }

 

        public void GetDataAsync(int value)

        {

            //if you need to simulate a long running event spin up a thread call this event after a wait

            GetDataCompleted(null, new GetDataCompletedEventArgs(new object[] { mockData }, null, false, null));

        }

 

        public event EventHandler<ServiceReference.GetDataCompletedEventArgs> GetDataCompleted;

    }

 

 

And finally add a service factory interface and two implementations so we have a consistent way for the client to get an instance of the mock or real service:

 

    public interface IServiceAdapterFactory

    {

        IServiceAdapter Create(Binding binding, EndpointAddress endpoint);       

    }

 

    public class ServiceAdapterFactory : IServiceAdapterFactory

    {

        public IServiceAdapter Create(Binding binding, EndpointAddress endpoint)

        {

            return new ServiceAdapter(new ServiceClient(binding, endpoint));

        }

    }

 

    public class MockServiceAdapterFactory : IServiceAdapterFactory

    {

        public IServiceAdapter Create(Binding binding, EndpointAddress endpoint)

        {

            int mockData = 10;

            return new MockServiceAdapter(mockData);

        }

    }

 

The Result

Now we can pass the appropriate instance of IServiceAdapterFactory into our silverlight model and replace all uses of the ServiceClient with a call to the factory’s Create method:

 

    public partial class MainPage : UserControl

    {

        bool useMock = true;

 

        IServiceAdapterFactory serviceFactory;

 

        public MainPage()

        {

            InitializeComponent();

 

            if (useMock)

            {

                serviceFactory = new MockServiceAdapterFactory();

            }

            else

            {

                serviceFactory = new ServiceAdapterFactory();

            }

 

            IServiceAdapter client = serviceFactory.Create(new BasicHttpBinding(), new EndpointAddress("http://service.com/service.svc"));

            client.GetDataCompleted += new EventHandler<GetDataCompletedEventArgs>

                (

                    (sender,e) => Console.WriteLine(e.Result)

                );

 

            client.GetDataAsync(10);

        }

    }