Search code examples
c#wcfasynchronousasynchronous-wcf-call

Asynchronous WCF Query Final step


I am trying to learn how to implement an asynchronous pattern to query multiple wcf services concurrently, but do not know how to check that all concurrent calls are complete. I have a class that executes the asynchronous operations and then adds to a List as operations complete:

   public static class ODataAsync
    {
        static DataServiceContext ServiceContext;
        static List<DynamicEntity> Results = new List<DynamicEntity>(); 

        private static void GetAsync(string serviceUri, NameValueCollection queryOptions, IAuthenticationScheme authenticationScheme)
        {
            string baseUri;
            string entitySet;
            string entityKey;
            string queryString;
            ValidateServiceUri(serviceUri, out baseUri, out entitySet, out entityKey, out queryString);
            string resource = !string.IsNullOrEmpty(entityKey) ? entitySet + "(" + entityKey + ")" : entitySet;

            DataServiceContext context = new DataServiceContext(new Uri(baseUri));
            context.IgnoreMissingProperties = true;

            ServiceContext = context; 

            DataServiceContextHandler handler = new DataServiceContextHandler(authenticationScheme);
            handler.HandleGet(context);

            DataServiceQuery<EntryProxyObject> query = context.CreateQuery<EntryProxyObject>(resource);

            NameValueCollection options = HttpUtility.ParseQueryString(queryString);
            options.Add(queryOptions);

            foreach (string key in options.AllKeys)
            {
                query = query.AddQueryOption(key, options[key]);
            }

            try
            {
                query.BeginExecute(GetAsyncComplete, query);
            }
            catch (DataServiceQueryException ex)
            {
                throw new ApplicationException("An error occurred during query execution.", ex); 
            }
        }

        private static void GetAsyncComplete(IAsyncResult result)
        {
            QueryOperationResponse<EntryProxyObject> response = 
                ((DataServiceQuery<EntryProxyObject>)result).EndExecute(result) as QueryOperationResponse<EntryProxyObject>; 

            IList<dynamic> list = new List<dynamic>(); 

            foreach (EntryProxyObject proxy in response)
            {
                DynamicEntity entity = new DynamicEntity(proxy.Properties);
                Results.Add(entity);
            }

            while (response.GetContinuation() != null)
            {
                Uri uri = response.GetContinuation().NextLinkUri;

                response = ServiceContext.Execute<EntryProxyObject>(uri) as QueryOperationResponse<EntryProxyObject>;

                foreach (EntryProxyObject proxy in response)
                {
                    DynamicEntity entity = new DynamicEntity(proxy.Properties);
                    Results.Add(entity);
                }
            }
        }    
    }

My 2 questions are:

1) How do I ensure that I only get the List Result(s) when all concurrent calls are finished? For instance if call GetAsync() within a loop, starting several concurrent processes, I need to ensure that they are all finished before taking the data out of the List Result.

2) Can I use BeginExecute() inside the GetContinuation() call and recursively use the same method GetAsyncComplete() as the callback function ? Or would that create a ton of threads and actually slow things down.

Thank you.


Solution

  • Have a look at the static WaitHandle.WaitAll(WaitHandle[] waitHandles) method. There's a great code example on that page.

    Typically, any class implementing the asynchronous pattern will define methods of the form:

    IAsyncResult BeginXXX(AsyncCallback callback, Object state);
    
    Result EndXXX(IAsyncResult asyncResult);
    

    Calling BeginXXX invokes the method asynchronously, and calling EndXXX forces the current thread to wait until the asynchronous method completes.

    To make multiple asynchronous calls, all you need to do is call BeginXXX as many times as necessary, and then call WaitHandle.WaitAll(...) passing in the IAsyncResult.AsyncWaitHandle for all of the calls that you want to wait for.

    To answer your second question, yes you can use GetAsynComplete recursively. The state parameter given to BeginXXX call is used to identify the request, and can be accessed via the IAsyncResult.UserState property - so you can match a request to a response.

    Whether or not this is a good thing is a bit more subjective. Usually, async calls go via a threadpool, so creating lots of them at once can exhaust the available threads and after that, new calls get queued until a thread becomes available. There is some overhead in switching between threads, but that is probably not going to be significant (unless you have a very large number of threads), compared to the time taken to do it synchronously.