【问题标题】:Best practices for converting WCF calls to async WCF calls将 WCF 调用转换为异步 WCF 调用的最佳实践
【发布时间】:2009-03-10 20:26:14
【问题描述】:

我在尝试将所有正常 WCF 调用转换为异步 WCF 调用时遇到问题。我发现我重构了很多代码,但不确定到底该怎么做。我使用了我找到的方法here,但遇到了我需要按顺序发生的问题。

private void btnSave_Click(object sender, RoutedEventArgs e)
{

  List<Item> itemList = GetList();
  foreach(Item i in itemList)
  {
    DoSomeWork(i);

    if(i.SomeID == 0)
    {      
       DoSomeMoreWork(i);  
    }

    UpdateRecord(i)  // this can't execute until the above code is complete

  }
}

private void DoSomeWork(Item i)
{
  // call async method
}

private void DoSomeMoreWork(i)
{
  // call async method
}

private void UpdateRecord(item i)
{
  // call async method
}

重构代码以异步方式工作的最佳方法是什么,还是我需要完全重新考虑我的逻辑?我真的必须在各处插入计数器和开关以确保某些事情在其他事情执行之前完成吗?

编辑:我这样做的原因是在接下来的几个月内,我们正在将此 WPF 应用程序转换为 Silverlight,这需要异步调用。因此,我正在尝试将我们的常规 WCF 调用转换为 async 以备不时之需。我发现它需要一种不同的思维方式。

【问题讨论】:

    标签: wcf asynchronous


    【解决方案1】:

    对于你正在做的事情,我想说处理事情的真正地方是对每个项目的服务进行一次调用,而不是 3 个。

    如果项目列表不是很大,最好使用整个列表对服务进行一次调用...

    private void btnSave_Click(object sender, RoutedEventArgs e)
    {  
        List<Item> itemList = GetList();  
        foreach(Item i in itemList)  
        {    
            DoAllTheWorkAndUpdate(i);    
        }
    }
    

    或者...

    private void btnSave_Click(object sender, RoutedEventArgs e)
    {  
        List<Item> itemList = GetList();  
        foreach(Item i in itemList)  
        {    
            if(i.Id == 0)
            {
                DoLotsOfWorkAndUpdate(i);
            }
            else
            {
                DoSomeWorkAndUpdate(i);
            }
    
        }
    }
    

    或者...

    private void btnSave_Click(object sender, RoutedEventArgs e)
    {  
        List<Item> itemList = GetList();  
        DoTheWorkOnTheWholeList(itemList);
    }
    

    换句话说,感觉您的某些职责可能放错了位置 - 我通常更喜欢提供服务,以便我可以给他们打一个电话。然后,异步性质无关紧要,因为您没有执行一系列事件。

    【讨论】:

    • urm 那么如果你在 3 个独立的服务器上调用 3 个独立的服务呢?很可能是 2,但由于我在扮演魔鬼倡导者,所以我会说 3。例如,我必须在 Authorize.NET 的 CIM Web 服务中创建付款配置文件,然后将结果发送到我自己的服务。
    • 好吧,在这种情况下,您显然必须至少进行 n 次调用,其中 n 是单独服务的数量。我仍然会尝试将其保留为每个单独的服务的单个呼叫。此外,如果您将数据从一项服务传递到另一项服务,则可能是职责错位的迹象——让其中一项服务为您联系另一项服务可能是更好的解决方案。当然,这可能并不总是可行的,但是要求消费者代码保持其他两个服务同步是需要小心完成的事情 - 有很大的出错空间。
    【解决方案2】:

    查看 Juval Lowy(Programming WCF Services 的作者)website 的示例,了解如何在 WCF 中实现异步编程。下载是免费的;您只需提供您的电子邮件地址。

    【讨论】:

      【解决方案3】:

      当您需要在循环内同步时,为什么需要使用异步 WCF 操作,我可能有点困惑。

      如果您只是使用异步方法来帮助防止 UI 挂起,那么您可以只使用支持进度更新的 BackgroundWorker 以使 UI 保持最新,而不使用异步 WCF 调用。

      您还应该能够从 Async 方法的 Completed 事件中调用各种函数。

      只需将事件处理程序连接到已完成的事件,然后在启动异步 WCF 调用时将您的 Item 对象作为 userState 参数传递。这样,您将在每个 Completed 事件触发时将其作为参数。这样,您将只在上一个异步调用完成时执行下一步的处理。

      我不知道这是否真的回答了你的问题。

      【讨论】:

      • 当您真正喜欢同步时使用异步方法的至少一个原因是:当您使用 Silverlight 时,一切 都必须是异步的。这通常是有充分理由的,但它 PITA。
      • +1 表示肯·史密斯的评论;我撞到了同一堵墙stackoverflow.com/questions/1286864/…
      【解决方案4】:

      【讨论】:

        【解决方案5】:

        如果您不使用 Silverlight,您可以在一个方法中阻止您的线程,直到其他方法完成,例如使用 ManualResetEvent。但这在 Silverlight 中不起作用,因为所有 WCF 调用都发生在主 UI 线程上,因此如果您阻止该线程,一切 都会阻止。更好的方法是使用回调来做这样的事情:

            public delegate void OperationCallback();
        
            private void btnSave_Click(object sender, RoutedEventArgs e)
            {
        
                List<Item> itemList = GetList();
                foreach (Item i in itemList)
                {
                    DoSomeWork(i, () =>
                    {
                        if (i.SomeID == 0)
                        {
                            DoSomeMoreWork(i, () =>
                            {
                                UpdateRecord(i);
                            });
                        }
                        else
                        {
                            UpdateRecord(i);
                        }
                    });
        
                }
            }
        
            private void DoSomeWork(Item i, OperationCallback callback)
            {
                // call async method then callback when it completes.
                callback();
            }
        
            private void DoSomeMoreWork(Item i, OperationCallback callback)
            {
                // call async method, then callback when it completes.
                callback();
            }
        
            private void UpdateRecord(Item i)
            {
                // call async method
            }
        

        当然不如同步版本清晰,但如果尽可能多地使用 lambda 表达式,仍然可以保持控制流的可读性。

        【讨论】:

          【解决方案6】:

          为 Item 添加 2 个属性 SomeWorkDone 和 SomeMoreWorkDone 都作为布尔值。创建处理 DoSomeWorkCompleted 和 DoSomeMoreWorkCompleted 的方法。在这些方法中,将相应的布尔属性设置为 true 并调用 UpdateRecord。在 UpdateRecord 中,确保两个 Done 属性都为 true,然后完成调用。

          您可能会遇到一些争用问题,但这应该能让您继续前进。

          【讨论】:

          • 除非我有误解,否则我认为这行不通。您要么需要阻止一个方法,直到其他方法完成(在这种情况下,您将使用某种 ResetEvent),或者您需要在另一种方法完成某种回调时调用一个方法。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-03-20
          • 2016-03-27
          • 2019-01-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多