C# 从UI线程通过异步任务调用web服务会导致死锁

C# 从UI线程通过异步任务调用web服务会导致死锁,c#,xamarin,async-await,deadlock,xamarin.forms,C#,Xamarin,Async Await,Deadlock,Xamarin.forms,我是Xamarin表单的新手,有一个应用程序可以异步获取用户的事务,但在调用web服务时遇到了死锁 我有一个TransactionView: 这是XAML: <Label Text="Account:" Grid.Row="0" Grid.Column="0" Style="{StaticResource LabelStyle}"/> <ctrls:BindablePicker x:Name="ddlAccountsWinPhone" ItemsSource="{Bindi

我是Xamarin表单的新手,有一个应用程序可以异步获取用户的事务,但在调用web服务时遇到了死锁

我有一个TransactionView:

这是XAML:

<Label Text="Account:" Grid.Row="0" Grid.Column="0" Style="{StaticResource LabelStyle}"/> 
<ctrls:BindablePicker x:Name="ddlAccountsWinPhone" ItemsSource="{Binding Accounts}" SelectedIndex="{Binding AccountID}"  Grid.Row="0" Grid.Column="1" />
<Label Text="From Date:" Grid.Row="2" Grid.Column="0" Style="{StaticResource LabelStyle}"/> <DatePicker x:Name="dtFromWinPhone" Date="{Binding FromDate}" Grid.Row="2" Grid.Column="1"  /> 
<Label Text="To Date:" Grid.Row="3" Grid.Column="0" Style="{StaticResource LabelStyle}"/> <DatePicker x:Name="dtToWinPhone" Date="{Binding ToDate}"  Grid.Row="3" Grid.Column="1" /> 
<Button x:Name="btnViewWinPhone" Text="View" Grid.Row="5" Grid.Column="0" Grid.ColumnSpan="2"  
Command ="{Binding ShowTransactions}" />
以下是TransactionViewModel:

public class TransactionViewModel : ViewModelBase
{
    private int accountID;
    private List<Account> accounts = new List<Account>();
    private Account accountItemSelected;
    private DateTime fromDate = new DateTime(1900,1,1);
    private DateTime toDate = new DateTime(1900, 1, 1);

    private ContentPage transactionGridViewNav;

    public TransactionViewModel(INavigation navigationInterface, ContentPage transactionSubPageView)
    {
        base.navigation = navigationInterface;
        transactionSubPageViewNav = transactionSubPageView; //I SAVE A REFERENCE TO THE SUBPAGE HERE
        accounts.AddRange(Client.Accounts);
    }

    public ICommand ShowTransactions
    {
        get
        {
            return new Command(async () =>
            { 
                //HERE IS WHERE "I THINK" I WANT TO FETCH THE TRANSACTIONS

                //THEN NAVIGATE TO THE THE SUBPAGE WITH THE GRID OF TRANSACTIONS
                await navigation.PushAsync(transactionSubPageViewNav);
            });
        }
    }

    public int AccountID
    {
        get{ return this.accountID; }
        set{
            this.accountID = value;
            this.OnPropertyChanged("AccountID");
            }
    }

    public List<Account> Accounts
    {
        get{
            return this.accounts;}
        set{
            this.accounts = value;
            this.OnPropertyChanged("Accounts");
            }
    }

    public Account AccountItemSelected
    {
        get{return accountItemSelected;}
        set {
                if (accountItemSelected != value)
                {
                    accountItemSelected = value;
                    OnPropertyChanged("AccountItemSelected");
                }
            }
    }

    public DateTime FromDate { get; set; }
    public DateTime ToDate { get; set; }}
    ...
public class TransactionSubPageViewModel : ViewModelBase
{
    public List<Transaction> transactionsGrid = new List<Transaction>();

    public int accountId = 1636;
    public DateTime fromDate = new DateTime(2015, 8, 1);
    public DateTime toDate = new DateTime(2015, 9, 1);

    public TransactionGridViewModel() { }

    public List<Transaction> TransactionsGrid
    {
        get {
            if (transactionsGrid == null) transactionsGrid = MyWebService.GetTransactions(1636, new DateTime(2015, 8, 1), new DateTime(2015, 9, 1)).Result;
            return transactionsGrid;}
    }
}
这是子页面ViewModel:

public class TransactionViewModel : ViewModelBase
{
    private int accountID;
    private List<Account> accounts = new List<Account>();
    private Account accountItemSelected;
    private DateTime fromDate = new DateTime(1900,1,1);
    private DateTime toDate = new DateTime(1900, 1, 1);

    private ContentPage transactionGridViewNav;

    public TransactionViewModel(INavigation navigationInterface, ContentPage transactionSubPageView)
    {
        base.navigation = navigationInterface;
        transactionSubPageViewNav = transactionSubPageView; //I SAVE A REFERENCE TO THE SUBPAGE HERE
        accounts.AddRange(Client.Accounts);
    }

    public ICommand ShowTransactions
    {
        get
        {
            return new Command(async () =>
            { 
                //HERE IS WHERE "I THINK" I WANT TO FETCH THE TRANSACTIONS

                //THEN NAVIGATE TO THE THE SUBPAGE WITH THE GRID OF TRANSACTIONS
                await navigation.PushAsync(transactionSubPageViewNav);
            });
        }
    }

    public int AccountID
    {
        get{ return this.accountID; }
        set{
            this.accountID = value;
            this.OnPropertyChanged("AccountID");
            }
    }

    public List<Account> Accounts
    {
        get{
            return this.accounts;}
        set{
            this.accounts = value;
            this.OnPropertyChanged("Accounts");
            }
    }

    public Account AccountItemSelected
    {
        get{return accountItemSelected;}
        set {
                if (accountItemSelected != value)
                {
                    accountItemSelected = value;
                    OnPropertyChanged("AccountItemSelected");
                }
            }
    }

    public DateTime FromDate { get; set; }
    public DateTime ToDate { get; set; }}
    ...
public class TransactionSubPageViewModel : ViewModelBase
{
    public List<Transaction> transactionsGrid = new List<Transaction>();

    public int accountId = 1636;
    public DateTime fromDate = new DateTime(2015, 8, 1);
    public DateTime toDate = new DateTime(2015, 9, 1);

    public TransactionGridViewModel() { }

    public List<Transaction> TransactionsGrid
    {
        get {
            if (transactionsGrid == null) transactionsGrid = MyWebService.GetTransactions(1636, new DateTime(2015, 8, 1), new DateTime(2015, 9, 1)).Result;
            return transactionsGrid;}
    }
}
如果我从子页面的
OnAppearing
事件调用
GetTransactions
webservice,它会挂起,如果我从
ICommand ShowTransactions调用它,它也会挂起。我是否缺少
等待
继续

我和一些人在一起,我知道我是,但我只是不知道如何解决它


我尝试了
configurewait(false)
但运气不佳。如果我能在页面中的a上调用Web服务,那就太理想了。

成功!!我知道这将是一个在这里询问的案例,然后我会解决它。这真有趣

这就是我如何让它工作的。请随意评论它:


我将TransactionSubPageViewModel的TransactionGrid
属性转换为异步方法,并将webservice调用直接放在其中:

public async Task<List<Transaction>> TransactionsGrid()
{
    if (transactionsGrid == null)
    {
        var client = new System.Net.Http.HttpClient(new ModernHttpClient.NativeMessageHandler());
        client.BaseAddress = new Uri("http://theWebAddress.com/);
        var response = await client.GetAsync("API.svc/Transactions/" + accountId + "/" + fromDate.ToString("yyyy-MM-dd") + "/" + toDate.ToString("yyyy-MM-dd"));
        var transactionJson = await response.Content.ReadAsStringAsync(); //DONT USE Result HERE, USE AWAIT AS PER @Noseratio suggested
        var transactions = Newtonsoft.Json.JsonConvert.DeserializeObject<List<Transaction>>(transactionJson);
        transactionsGrid = transactions;
    }
        return transactionsGrid;
}
然后,当我从OnAppearing()调用PopulateGridTransactions时,数据已经可用:

public void PopulateGridTransactions()
{
...
  foreach (Models.Transaction transaction in transactionSubPageViewModel.TransactionsGrid().Result)
  {
编辑:

正如@Noseratio所指出的,让我解释一下为什么需要在异步中进行Try/Catch

巧合的是,在从web服务获得响应后,我在下一行反序列化json web服务结果时出错

我在Xamarin App.cs中设置了一个全局异常处理程序,以便它能够捕获它:

通过在异步中放置Try/Catch,我可以捕获同一帧上的异常(当
应用程序捕获未处理的异常时,在堆栈展开之前,即):


OT,当您将
async void
lambda传递到
new命令(async()=>{…})
中时,您应该在其中执行一些异常处理。它们不会在同一堆栈帧上抛出(与非异步lambda一样)。此外,
response.Content.ReadAsStringAsync().Result
可能会导致另一个死锁。将其更改为
等待响应.Content.ReadAsStringAsync()
。在别处查找
.Result
.Wait()
。不,将
try/catch
放入
async()
lambda中。试试这个:
Try{Action lambda=async()=>{throw new ApplicationException();}}}catch{Debug.Assert(false);}
。你永远无法到达终点。更多。谢谢你,我真的很感激这两条建议!干杯:)我尽力向下一个需要理解这一点的人解释。
public async Task<List<Transaction>> TransactionsGrid()
{
    if (transactionsGrid == null)
    {
        var client = new System.Net.Http.HttpClient(new ModernHttpClient.NativeMessageHandler());
        client.BaseAddress = new Uri("http://theWebAddress.com/);
        var response = await client.GetAsync("API.svc/Transactions/" + accountId + "/" + fromDate.ToString("yyyy-MM-dd") + "/" + toDate.ToString("yyyy-MM-dd"));
        var transactionJson = await response.Content.ReadAsStringAsync(); //DONT USE Result HERE, USE AWAIT AS PER @Noseratio suggested
        var transactions = Newtonsoft.Json.JsonConvert.DeserializeObject<List<Transaction>>(transactionJson);
        transactionsGrid = transactions;
    }
        return transactionsGrid;
}
public ICommand ShowTransactions
{
    get
    {
        return new Command(async () =>
        { 
            //THIS IS WHERE I WAS MISSING THE AWAIT!!!
            await ((TransactionGridViewModel)transactionSubPageViewNav.BindingContext).TransactionsGrid();

            await navigation.PushAsync(transactionSubPageViewNav);
        });
    }
public void PopulateGridTransactions()
{
...
  foreach (Models.Transaction transaction in transactionSubPageViewModel.TransactionsGrid().Result)
  {