views:

464

answers:

1

Is there any existing plumbing to run WCF calls in batches in a BackgroundWorker?

Obviously since all Silverlight WCF calls are async - if I run them all in a backgroundworker they will all return instantly.

I just don't want to implement a nasty hack if theres a nice way to run service calls and collect the results.

  • Doesnt matter what order they are done in
  • All operations are independent
  • I'd like to have no more than 5 items running at once

Edit: i've also noticed (when using Fiddler) that no more than about 7 calls are able to be sent at any one time. Even when running out-of-browser this limit applies. Is this due to my default browser settings - or configurable also. obviously its a poor man's solution (and not suitable for what i want) but something I'll probably need to take account of to make sure the rest of my app remains responsive if i'm running this as a background task and don't want it using up all my connections.

+3  A: 

I think your best bet would be to have your main thread put service request items into a Queue that is shared with a BackgroundWorker thread. The BackgroundWorker can then read from the Queue, and when it detects a new item, initiate the async WCF service request, and setup to handle the AsyncCompletion event. Don't forget to lock the Queue before you call Enqueue() or Dequeue() from different threads.

Here is some code that suggests the beginning of a solution:

using System;
using System.Collections.Generic;
using System.ComponentModel;

namespace MyApplication
{
    public class RequestItem
    {
        public string RequestItemData { get; set; }
    }

    public class ServiceHelper
    {
        private BackgroundWorker _Worker = new BackgroundWorker();
        private Queue<RequestItem> _Queue = new Queue<RequestItem>();
        private List<RequestItem> _ActiveRequests = new List<RequestItem>();
        private const int _MaxRequests = 3;

        public ServiceHelper()
        {
            _Worker.DoWork += DoWork;
            _Worker.RunWorkerAsync();
        }

        private void DoWork(object sender, DoWorkEventArgs e)
        {
            while (!_Worker.CancellationPending)
            {
                // TBD: Add a N millisecond timer here
                //      so we are not constantly checking the Queue

                // Don't bother checking the queue
                // if we already have MaxRequests in process
                int _NumRequests = 0;
                lock (_ActiveRequests)
                {
                    _NumRequests = _ActiveRequests.Count;
                }
                if (_NumRequests >= _MaxRequests)
                    continue;

                // Check the queue for new request items
                RequestItem item = null;
                lock (_Queue)
                {
                    RequestItem item = _Queue.Dequeue();
                }
                if (item == null)
                    continue;

                // We found a new request item!
                lock (_ActiveRequests)
                {
                    _ActiveRequests.Add(item);
                }

                // TBD: Initiate an async service request,
                //      something like the following:
                try
                {
                    MyServiceRequestClient proxy = new MyServiceRequestClient();
                    proxy.RequestCompleted += OnRequestCompleted;
                    proxy.RequestAsync(item);
                }
                catch (Exception ex)
                {
                }
            }
        }

        private void OnRequestCompleted(object sender, RequestCompletedEventArgs e)
        {
            try
            {
                if (e.Error != null || e.Cancelled)
                    return;

                RequestItem item = e.Result;

                lock (_ActiveRequests)
                {
                    _ActiveRequests.Remove(item);
                }
            }
            catch (Exception ex)
            {
            }
        }

        public void AddRequest(RequestItem item)
        {
            lock (_Queue)
            {
                _Queue.Enqueue(item);
            }
        }
    }
}

Let me know if I can offer more help.

Good luck,
Jim McCurdy
Face to Face Software and YinYangMoney

Jim McCurdy
thanks jim - except you didn't address the most important thing which is that i dont want all requests to run at once. i supposed the millisecond timer should just check if ActiveRequests >=3 though. i like the idea of a queue. one of those generic classes i always forget about
Simon_Weaver
Simon,You don't have to run all the requests at once. The timer I suggested is merely to avoid continuous checking of the Queue.I updated my the answer code above by checking for _MaxRequests, and by including a sample call to an async service, with a corresponding Completed event handler. Hopefully this gets you a bit closer.
Jim McCurdy