开发者

Is there an event to tell when a WCF auto generated proxy is performing an async operation?

开发者 https://www.devze.com 2022-12-16 18:53 出处:网络
I\'m creating a Silverlight application and I have a couple of issues with the aut开发者_开发问答ogenerated proxy (by going add service reference in Visual Studio 2008)

I'm creating a Silverlight application and I have a couple of issues with the aut开发者_开发问答ogenerated proxy (by going add service reference in Visual Studio 2008)

When the proxy is generated on the client the async methods are generated and I can call them and that's fine.

But what I would really like to do is have a 'busy' or 'async loading' animation on my page. I've achieved this in the past by having a static class that stores the number of async calls currently being made - The problem with this approch is that I would have to remember to manualy increment and decrement the count whenever I called the async method on the proxy (and decrement it when it was completed) - and of course I'd forget to do this rather often.

The other approach I tried which was slightly better was to wrap the proxy in my own wrapper class so instead of calling methods on the proxy I'd call methods on the wrapper and the wrapper would increment/decrement the count - This was a bit better, but it proved to be a bit of a pain when the operation contracts/names/parameters of my service kept changing esp. when the service was in early development.

Am I missing something? Because I really think it should be a bit simpler than my current approach.


I agree, there should be something easier than your current approach. But until we can use T4 templates to generate the proxy classes, or unless you want to roll your own (not recommended IMO, YMMV), something along the lines of what you've already tried is your best shot, I think.

I'm currently using a hybrid of both approaches. I've wrapped all WCF calls inside a series of data access classes that translate the not-very-helpful event-driven model that MS provides into a much simpler callback approach. Within these data access classes, I've also wrapped each call with a static PreProcessCall() method that handles incrementing an outstanding call counter and marshaling the call onto a background thread; and I've wrapped each return call with a static PostProcessCall() method that decrements the outstanding call counter and marshals the callback onto the UI thread.

    public static void PreProcessCall(Action action)
    {
        Logger.LogDebugMessage("Pre-processing call for {0}", (new StackTrace()).GetFrame(1).GetMethod().Name);
        Interlocked.Increment(ref pendingCalls);
        ThreadPool.QueueUserWorkItem(o =>
            {
                try
                {
                    action();
                }
                catch (System.Exception ex)
                {
                    DataPublisher.GetPublisher().RaiseDataProcessExceptionOccurred(ex, ex.Message);
                    UpdatePendingCalls();
                }
            });
    }

    private static void UpdatePendingCalls()
    {
        Interlocked.Decrement(ref pendingCalls);
        Debug.Assert(pendingCalls >= 0, "The number of pending calls should never go below zero.");
        if (pendingCalls <= 0)
        {
            lock (pendingCallNotifications)
            {
                while (pendingCallNotifications.Count > 0)
                {
                    Action callback = pendingCallNotifications.Dequeue();
                    Globals.Dispatcher.BeginInvoke(callback);
                }
            }
        }
    }

    public static void PostProcessCall(Action action)
    {
        Logger.LogDebugMessage("Post-processing call for {0}", (new StackTrace()).GetFrame(1).GetMethod().Name);
        UpdatePendingCalls();
        Globals.Dispatcher.BeginInvoke(() =>
            {
                try
                {
                    action();
                }
                catch (System.Exception ex)
                {
                    DataPublisher.GetPublisher().RaiseDataProcessExceptionOccurred(ex, ex.Message);
                }
            });
    }

So a typical call looks something like this:

    public void SendMessage(string message, OperationCallback callback)
    {
        DataConnectionManager.PreProcessCall(() =>
            {
                Logger.LogDebugMessage("SendChatMessage");
                notificationClient.SendChatMessageAsync(roomViewModel.SessionId, message, callback);
            });
    }

    void RoomService_SendChatMessageCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
    {
        OperationCallback callback = (OperationCallback)e.UserState;
        DataConnectionManager.PostProcessCall(() =>
            {
                if (callback != null)
                {
                    callback(e.Error);
                }
                Logger.LogDebugMessage("SendChatMessageCompleted.");
            });
    }

Like I said, basically what you've already tried.


I created an approach just recently that solves this problem See: Dynamic IL method causes "Operation could destabilize the runtime"

Basically, create a wrapper for calling your async method and let that wrapper register a general purpose event using dynamic methods and reflection. Then you never have to worry about maintaining a wrapper for every async call in your wrapper.

0

精彩评论

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

关注公众号