使用Web服务的异步方法

时间:2012-11-02 17:20:34

标签: .net wcf silverlight asynchronous .net-4.5

我想编写一个Silverlight应用程序,其中有三个DataGrids。 每个Datagrid都使用异步方法从Web服务获取其数据。 现在,我希望第一个数据网格从Web服务获取数据,而不是从第一个数据网格中的选定行和第一个数据网格中的参数获取第二个数据网格中来自Web服务的数据。 第一个数据网格通过注册事件处理程序而不是使用异步方法来获取MainPage方法中的数据。

现在我的问题是我在SelectionChanged(事件处理)方法中使用异步方法用于其他数据网格,我猜这个概念是错误的,因为在datagrid 2中选择了一些内容并返回到datagrid 1之后datagrids dissapear。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.ServiceModel;
using Rebat.SymptomeService;


namespace Rebat
{
    public partial class MainPage : UserControl
    {
        ServiceClient client = new ServiceClient();

        public MainPage()
        {
            InitializeComponent();
            ServiceClient client = new ServiceClient();
           client.SymptomeListCompleted += new EventHandler<SymptomeListCompletedEventArgs>(client_SymptomeListCompleted);
            client.SymptomeListAsync();
        }


        void client_SymptomeListCompleted(object sender, SymptomeListCompletedEventArgs e)
        {
            CustomerGrid.ItemsSource = e.Result;
        }
        void client_CustomerListCompleted(object sender, CustomerListCompletedEventArgs e)
        {
           CustomerGrid2.ItemsSource = e.Result;
        }
        void client_SalzListCompleted(object sender, SalzListCompletedEventArgs e)
        {
            SalzGrid.ItemsSource = e.Result;
        }

        private void CustomerGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            Symptome sympt = CustomerGrid.SelectedItem as Symptome;
            client.CustomerListCompleted += new EventHandler<CustomerListCompletedEventArgs>(client_CustomerListCompleted);
            client.CustomerListAsync(sympt.sId.ToString());

        }

        private void CustomerGrid2_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            Symptome2 sympt2 = CustomerGrid2.SelectedItem as Symptome2;
            client.SalzListCompleted += new EventHandler<SalzListCompletedEventArgs>(client_SalzListCompleted);
            //this is the PROBLEM:
            client.SalzListAsync(sympt2.sy1.ToString(), sympt2.sy2.ToString());
        }

    }
}

我需要更改什么,或者如何使用异步方法? 你能在事件处理方法中使用异步方法吗?这是否适用于使用Web服务?

1 个答案:

答案 0 :(得分:0)

假设您可以使用.NET 4.5异步功能,如果您将serviceproxy调整为基于任务的异步模式,那么执行此类调用会简单得多。 您可以在事件处理程序中使用异步方法,您只需要使其异步,以便从新的async / await关键字中充分受益。

要调整代理,首先声明一个帮助程序静态类:

public static partial class TAPExtensions
{
    public static void HandleCompletion<T>(TaskCompletionSource<T> tcs, AsyncCompletedEventArgs args, Func<T> getResult, Action unregisterHandler = null)
    {
        // Transfers the results from the AsyncCompletedEventArgs and getResult() to the 
        // TaskCompletionSource, but only AsyncCompletedEventArg's UserState matches the TCS 
        // (this check is important if the same WebClient is used for multiple, asynchronous 
        // operations concurrently).  Also unregisters the handler to avoid a leak. 
        if (args.UserState == tcs)
        {
            if (args.Cancelled) tcs.TrySetCanceled();
            else if (args.Error != null) tcs.TrySetException(args.Error);
            else tcs.TrySetResult(getResult());
            if (unregisterHandler != null) unregisterHandler();
        }
    }
}

现在,创建另一个帮助程序类来扩展代理。为每个操作创建一个适配器方法:

public static class ServiceClientEx
{
    public static async Task<IEnumerable<ReturnType>> SalzListTask(this ServiceClient proxy, string arg1, string arg2)
    {
        var tcs = new TaskCompletionSource<IEnumerable<ReturnType>>();
        EventHandler<SalzListCompletedEventArgs> handler = (s, args) => TAPExtensions.HandleCompletion(tcs, args, () => args.Result);
        proxy.SalzListCompleted += handler;

        try
        {
            proxy.SalzListAsync(arg1, arg2, tcs);
            return await tcs.Task;
        }
        finally { proxy.SalzListCompleted -= handler; }
    }
}

现在您可以在代码中使用扩展方法(假设您为ServiceClientEx类的命名空间添加了“using”指令)。结果将是这样的:

public partial class MainPage : UserControl
{
    ServiceClient client = new ServiceClient();

    public MainPage()
    {
        InitializeComponent();
        ServiceClient client = new ServiceClient();
    }


    private async void CustomerGrid2_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        Symptome2 sympt2 = CustomerGrid2.SelectedItem as Symptome2;

        alzGrid.ItemsSource = await client.SalzListTask(sympt2.sy1.ToString(), sympt2.sy2.ToString());
    }
}

请注意在eventhandler声明中使用“async”关键字。它会将异步行为传播给方法:一旦选择发生更改,处理程序将正常执行,直到它到达第一个await,此时它将返回到原始调用者(控件的调度程序)。当调用结束时,继续(将itemssource设置为操作结果)将在UIThread中执行。

您甚至可以打包调用以检查异常,例如:

private async void CustomerGrid2_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        try{
            Symptome2 sympt2 = CustomerGrid2.SelectedItem as Symptome2;

            alzGrid.ItemsSource = await client.SalzListTask(sympt2.sy1.ToString(), sympt2.sy2.ToString());
        }catch(MyException ex){
            MessageBox.Show(ex.ToString());
        }
    }

一些建议:

  • 我不认为从构造函数中调用异步调用是个好主意。例如,您可以在控件的Loaded事件中进行一些初始化后触发它(更好的方法是在ViewModel中执行此类操作,遵循MVVM模式并从UI中消耗它,但这是另一个主题)
  • 您现在的方式是,每次选择更改时,都会在您的代理中添加一个新的处理程序,因此您的处理程序将执行的次数超过必要的次数。
  • 每次拨打电话时都尝试实例化代理,因为如果代理出现故障,实例将无法使用。