是否有事件要告知WCF自动生成的代理何时执行异步操作?

时间:2010-01-19 21:55:21

标签: wcf silverlight

我正在创建一个Silverlight应用程序,我对自动生成的代理有一些问题(通过在Visual Studio 2008中添加服务引用)

当在客户端上生成代理时,会生成异步方法,我可以调用它们,这很好。

但我真正想做的是在我的页面上有一个'忙'或'异步加载'动画。我在过去通过一个静态类来实现这个目的,该类存储了当前正在进行的异步调用的数量 - 这个approch的问题是,每当我调用async方法时,我都必须记住手动增加和减少计数。代理(当它完成时减少它) - 当然我会忘记经常这样做。

我试过的另一种方法稍好一点就是将代理包装在我自己的包装器类中,所以不要在代理上调用方法,而是在包装器上调用方法,包装器会增加/减少计数 - 这是好一点,但是当我的服务的操作合同/名称/参数不断变化时,事实证明这有点痛苦。当服务处于早期开发阶段时。

我错过了什么吗?因为我认为它应该比我目前的方法更简单。

2 个答案:

答案 0 :(得分:0)

我同意,应该比你现在的方法更容易。但是直到我们可以使用T4模板来生成代理类,或者除非你想自己推出(不推荐的IMO,YMMV),我认为你最好的选择就是你最好的选择。

我目前正在使用两种方法的混合。我已经将所有WCF调用包含在一系列数据访问类中,这些类将MS提供的非常有用的事件驱动模型转换为更简单的回调方法。在这些数据访问类中,我还使用静态PreProcessCall()方法包装每个调用,该方法处理递增未完成的调用计数器并将调用封送到后台线程;并且我使用静态PostProcessCall()方法包装每个返回调用,该方法递减未完成的调用计数器并将回调编组到UI线程上。

    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);
                }
            });
    }

所以典型的调用看起来像这样:

    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.");
            });
    }

就像我说的,基本上你已经尝试过了。

答案 1 :(得分:0)

我最近创建了一种方法来解决这个问题 请参阅:Dynamic IL method causes "Operation could destabilize the runtime"

基本上,创建一个用于调用异步方法的包装器,并让该包装器使用动态方法和反射注册一个通用事件。然后,您永远不必担心为包装器中的每个异步调用维护一个包装器。