开发者

Silverlight - limit application to one WCF call at a time

Silverlight can only send a certain number of simultaneous WCF requests at a time. I am trying to serialize the requests that a particular section of my application is performing because I don't need them to run concurrently.

The problem is as follows (summary below): "WCF proxies in Silverlight applications use the SynchronizationContext of the thread from which the web service call is initiated to schedule the invocation of the async event handler when the response is received. When the web service call is initiated from the UI thread of a Silverlight application, the async event handler code will also execute on the UI thread." http://tomasz.janczuk.org/2009/08/improving-performance-of-concurrent-wcf.html

summary: basically, if you block the thread that is calling the async method, it will never get called.

I can't figure out the right model of threading this such which would give me what I want in a reasonable way.

My only other requirement is that I don't want the UI thread to block.

As far as I can see, what should work is if the UI thread has a worker thread which queues up the calls as Action delegates, then uses an AutoResetEvent to execute a task one at a time in yet another worker thread. There are two problems: 1) The thread that calls async can't block, because then asyn开发者_如何学JAVAc will never get called. In fact, if you put that thread into a wait loop, I've noticed it doesn't get called either 2) You need a way to signal from the completed method of the async call that it is done.

Sorry that was so long, thanks for reading. Any ideas?


I have used a class that i build on my own to execute load operations synchronous. With the class you can register multiple load operations of diffrent domaincontexts and then execute them one by one. You can provide an Action to the constructor of the class that gets called, when all operations are finished (successful or failed).

Here´s the code of the class. I think it´s not complete and you have to change it to match your expectations. Maybe it can help you in your situation.

  public class DomainContextQueryLoader {
    private List<LoadOperation> _failedOperations;
    private Action<DomainContextQueryLoader> _completeAction;
    private List<QueuedQuery> _pendingQueries = new List<QueuedQuery>();

    public DomainContextQueryLoader(Action<DomainContextQueryLoader> completeAction) {
        if (completeAction == null) {
            throw new ArgumentNullException("completeAction", "completeAction is null.");
        }
        this._completeAction = completeAction;
    }

    /// <summary>
    /// Expose the count of failed operations
    /// </summary>
    public int FailedOperationCount {
        get {
            if (_failedOperations == null) {
                return 0;
            }
            return _failedOperations.Count;
        }
    }

    /// <summary>
    /// Expose an enumerator for all of the failed operations
    /// </summary>
    public IList<LoadOperation> FailedOperations {
        get {
            if (_failedOperations == null) {
                _failedOperations = new List<LoadOperation>();
            }
            return _failedOperations;
        }
    }

    public IEnumerable<QueuedQuery> QueuedQueries {
        get {
            return _pendingQueries;
        }
    }

    public bool IsExecuting {
        get;
        private set;
    }

    public void EnqueueQuery<T>(DomainContext context, EntityQuery<T> query) where T : Entity {
        if (IsExecuting) {
            throw new InvalidOperationException("Query cannot be queued, cause execution of queries is in progress");
        }
        var loadBatch = new QueuedQuery() {
            Callback = null,
            Context = context,
            Query = query,
            LoadOption = LoadBehavior.KeepCurrent,
            UserState = null
        };

        _pendingQueries.Add(loadBatch);
    }

    public void ExecuteQueries() {
        if (IsExecuting) {
            throw new InvalidOperationException("Executing of queries is in progress");
        }

        if (_pendingQueries.Count == 0) {
            throw new InvalidOperationException("No queries are queued to execute");
        }

        IsExecuting = true;
        var query = DequeueQuery();
        ExecuteQuery(query);
    }

    private void ExecuteQuery(QueuedQuery query) {
        System.Diagnostics.Debug.WriteLine("Load data {0}", query.Query.EntityType);
        var loadOperation = query.Load();
        loadOperation.Completed += new EventHandler(OnOperationCompleted);
    }

    private QueuedQuery DequeueQuery() {
        var query = _pendingQueries[0];
        _pendingQueries.RemoveAt(0);
        return query;
    }

    private void OnOperationCompleted(object sender, EventArgs e) {
        LoadOperation loadOperation = sender as LoadOperation;
        loadOperation.Completed -= new EventHandler(OnOperationCompleted);

        if (loadOperation.HasError) {
            FailedOperations.Add(loadOperation);
        }

        if (_pendingQueries.Count > 0) {
            var query = DequeueQuery();
            ExecuteQuery(query);
        }
        else {
            IsExecuting = false;
            System.Diagnostics.Debug.WriteLine("All data loaded");
            if (_completeAction != null) {
                _completeAction(this);
                _completeAction = null;
            }
        }
    }

}

Update: I´ve just noticed that you are not using WCF RIA Services, so maybe this class will not help your.


There are some options:

- You can take a look at the Agatha-rrsl either by inspecting the implementation of it or by just using it instead of pure wcf. The framework allows you to queue requests. You can read more here.

- Another option is to use the Reactive extension. There is a SO example here and more info here and here.

- You can try the Power Thread library from Jeffrey Richter. He describes it on his book CLR via C#. You can find the library here. This webcast gives you some info about it.

- You can always roll your own implementation. The yield statement is a good help here. Error handling makes it very difficult to get the solution right.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜