I am a newbie to Xamarin Forms and have an App that fetch's Transactions asynchronously but it encounters a Deadlock when I call a web service.
I have a TransactionView:
This is the 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}" />
This is the TransactionView's code behind, note I'll show a grid of transactions on subpage called TransactionSubPageView:
public partial class TransactionView : ContentPage
{
private TransactionViewModel transactionViewModel;
private TransactionViewModel ViewModel
{
get {
if (transactionViewModel == null) transactionViewModel = new TransactionViewModel(
this.Navigation, new TransactionSubPageView()); //Pass sub page to ViewModel ctor
return transactionViewModel;
}
}
public TransactionView()
{
InitializeComponent();
BindingContext = ViewModel;
}
Here is the 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; }}
...
This is the TransactionSubPageView:
public partial class TransactionSubPageView : ContentPage
{
public TransactionSubPageViewModel transactionSubPageViewModel;
public TransactionSubPageViewModel ViewModel
{
get
{
if (transactionSubPageViewModel == null)
transactionSubPageViewModel = new TransactionSubPageViewModel();
return transactionGridViewModel;
}
}
private Grid gridTransactions;
public TransactionSubPageView()
{
InitializeComponent();
BindingContext = ViewModel;
}
protected async override void OnAppearing()
{
base.OnAppearing();
//THIS IS A VOID TO POPULATE A GRID AND SET THE PAGE'S CONTENT, IT USES
//transactionSubPageViewModel.TransactionsGrid!!
PopulateGridTransactions();
}
This is the SubPage ViewModel:
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;}
}
}
Lastly here is the WebService Call which is causing the problem:
public static async Task<List<Transaction>> GetTransactions(int accountId, DateTime fromDate, DateTime toDate)
{
var client = new System.Net.Http.HttpClient(new 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")); //.ConfigureAwait(false);
var transactionJson = response.Content.ReadAsStringAsync().Result;
var transactions = JsonConvert.DeserializeObject<List<Transaction>>(transactionJson);
return transactions;
}
Thanks for reading so far, the problem is this line in the webmethod call always hangs:
var response = await client.GetAsync("API.svc/Transactions/" + accountId + "/" + fromDate.ToString("yyyy-MM-dd") + "/" + toDate.ToString("yyyy-MM-dd"));
If I call the GetTransactions
webservice from the SubPage's OnAppearing
event it hangs, if I call it from ICommand ShowTransactions
it also hangs. Am I missing an await
or a continue
?
I've read a fair few documents with similar people and who are confused, I know I am encountering a deadlock but I just don't know how to fix it.
I've tried ConfigureAwait(false)
without luck. It would be ideal if I could just call the WebService on a background thread, show a progress bar and when the operation is complete render the results in the page.