C# Xamarin.Forms ViewModel中的异步方法不等待AzureServiceTokenProvider和SqlConnection初始化
(编辑以添加有关我打电话的更多详细信息) 我有一个Xamarin表单应用程序连接到Azure App Services中托管的.Net Core 2.2 web服务 在我的视图模型中,我有这样一个调用:C# Xamarin.Forms ViewModel中的异步方法不等待AzureServiceTokenProvider和SqlConnection初始化,c#,xamarin.forms,azure-sql-database,azure-web-app-service,.net-core-2.2,C#,Xamarin.forms,Azure Sql Database,Azure Web App Service,.net Core 2.2,(编辑以添加有关我打电话的更多详细信息) 我有一个Xamarin表单应用程序连接到Azure App Services中托管的.Net Core 2.2 web服务 在我的视图模型中,我有这样一个调用: private async Task GetItems() { var result = await itemsListFactory.GetItemsAsync() } public async Task<SqlConnection> GetOpe
private async Task GetItems() {
var result = await itemsListFactory.GetItemsAsync()
}
public async Task<SqlConnection> GetOpenConnectionAsync()
{
var accessToken = await new AzureServiceTokenProvider().GetAccessTokenAsync("https://database.windows.net/");
var connection = new SqlConnection(dbconnection) {
AccessToken = accessToken
};
await connection.OpenAsync();
return connection;
}
public async Task<SqlConnection> GetOpenConnectionAsync()
{
var accessToken = new AzureServiceTokenProvider().GetAccessTokenAsync("https://database.windows.net/").Result;
var connection = new SqlConnection(dbconnection) {
AccessToken = accessToken
};
connection.OpenAsync().Wait();
return connection;
}
这就叫:
public async Task<IEnumerable<IItemInfo>> GetItemsAsync() {
return await ItemList.GetItemListAsync();
}
[Fetch]
private async void DataPortal_Fetch() {
var rlce = RaiseListChangedEvents;
RaiseListChangedEvents = false;
IsReadOnly = false;
using (var ctx = Dal.DalFactory.GetManager()) {
var dal = ctx.GetProvider<IItemDal>();
List<ItemDto> list = null;
list = await dal.FetchAsync();
foreach (var item in list) {
Add(DataPortal.FetchChild<ItemInfo>(item));
}
}
IsReadOnly = true;
RaiseListChangedEvents = rlce;
}
public异步任务GetItemsAsync(){
返回wait-ItemList.GetItemListAsync();
}
这称为(CSLA业务对象):
公共静态异步任务GetItemListAsync(){
return wait DataPortal.FetchAsync();
}
这就叫:
public async Task<IEnumerable<IItemInfo>> GetItemsAsync() {
return await ItemList.GetItemListAsync();
}
[Fetch]
private async void DataPortal_Fetch() {
var rlce = RaiseListChangedEvents;
RaiseListChangedEvents = false;
IsReadOnly = false;
using (var ctx = Dal.DalFactory.GetManager()) {
var dal = ctx.GetProvider<IItemDal>();
List<ItemDto> list = null;
list = await dal.FetchAsync();
foreach (var item in list) {
Add(DataPortal.FetchChild<ItemInfo>(item));
}
}
IsReadOnly = true;
RaiseListChangedEvents = rlce;
}
[Fetch]
私有异步void DataPortal_Fetch(){
var rlce=RaiseListChangedEvents;
RaiseListChangedEvents=false;
IsReadOnly=false;
使用(var ctx=Dal.DalFactory.GetManager()){
var dal=ctx.GetProvider();
List=null;
list=wait dal.FetchAsync();
foreach(列表中的变量项){
添加(DataPortal.FetchChild(item));
}
}
IsReadOnly=true;
RaiseListChangedEvents=rlce;
}
这要求:
public async Task<List<ItemDto>> FetchAsync() {
var resultSet = new List<ItemDto>();
var connectionManager = ServiceLocator.Current.GetInstance<IAzureConnectionManager>();
using (var conn = await connectionManager.GetOpenConnectionAsync()) {
/* Reading from DB */
}
return resultSet;
}
公共异步任务FetchAsync(){
var resultSet=新列表();
var connectionManager=ServiceLocator.Current.GetInstance();
使用(var conn=await connectionManager.GetOpenConnectionAsync()){
/*从数据库读取*/
}
返回结果集;
}
AzureConnectionManager的实现如下所示:
private async Task GetItems() {
var result = await itemsListFactory.GetItemsAsync()
}
public async Task<SqlConnection> GetOpenConnectionAsync()
{
var accessToken = await new AzureServiceTokenProvider().GetAccessTokenAsync("https://database.windows.net/");
var connection = new SqlConnection(dbconnection) {
AccessToken = accessToken
};
await connection.OpenAsync();
return connection;
}
public async Task<SqlConnection> GetOpenConnectionAsync()
{
var accessToken = new AzureServiceTokenProvider().GetAccessTokenAsync("https://database.windows.net/").Result;
var connection = new SqlConnection(dbconnection) {
AccessToken = accessToken
};
connection.OpenAsync().Wait();
return connection;
}
公共异步任务GetOpenConnectionAsync()
{
var accessToken=等待新的AzureServiceTokenProvider()。GetAccessTokenAsync(“https://database.windows.net/");
var connection=newsqlconnection(dbconnection){
AccessToken=AccessToken
};
等待连接。OpenAsync();
回路连接;
}
但是,我第一次打这个电话(例如,当天的第一个电话,或在一段时间没有使用该服务后)没有收到任何结果。任何后续的电话似乎都能正常工作。我猜这与服务由于不活动而不得不采取一些“额外步骤”来返回数据有关
每当我调试web服务并在视图模型以及服务器端代码中设置断点时,这种怀疑似乎就得到了证实。每当服务的调用返回时没有记录,它几乎就像是从服务器提前返回一样,因为它返回到视图模型时没有数据,然后我的调试器在收到访问令牌后跳回服务器。因此,就好像我的代码决定不等待GetAccessTokenAsync和OpenAsync完成它们必须完成的操作,然后再返回到客户机
我可以通过将.Result添加到GetAccessTokenAsync()和.Wait()添加到OpenAsync()来修复此问题,如下所示:
private async Task GetItems() {
var result = await itemsListFactory.GetItemsAsync()
}
public async Task<SqlConnection> GetOpenConnectionAsync()
{
var accessToken = await new AzureServiceTokenProvider().GetAccessTokenAsync("https://database.windows.net/");
var connection = new SqlConnection(dbconnection) {
AccessToken = accessToken
};
await connection.OpenAsync();
return connection;
}
public async Task<SqlConnection> GetOpenConnectionAsync()
{
var accessToken = new AzureServiceTokenProvider().GetAccessTokenAsync("https://database.windows.net/").Result;
var connection = new SqlConnection(dbconnection) {
AccessToken = accessToken
};
connection.OpenAsync().Wait();
return connection;
}
公共异步任务GetOpenConnectionAsync()
{
var accessToken=new AzureServiceTokenProvider()。GetAccessTokenAsync(“https://database.windows.net/三、结果;
var connection=newsqlconnection(dbconnection){
AccessToken=AccessToken
};
connection.OpenAsync().Wait();
回路连接;
}
但这感觉像是一个黑客
我怀疑这是我应该解决这个问题的方式,但也许是这样。如果这是处理这种情况的正确方法,至少我想知道这里发生了什么
wait运算符暂停对封闭异步方法的求值,直到由其操作数表示的异步操作完成。异步操作完成后,await操作符返回操作的结果(如果有)。当wait运算符应用于表示已完成操作的操作数时,它会立即返回操作结果,而不会暂停封闭方法。await运算符不阻止计算异步方法的线程。当wait操作符挂起封闭的异步方法时,控件将返回给该方法的调用方
因此,如果我们看一下文档中关于Async/Wait的内容,您会注意到
当wait运算符应用于表示已完成操作的操作数时,它会立即返回操作结果,而不会暂停封闭方法
更有可能的是OpenAsync()代码>被视为已完成的操作数,因为您可能不等待返回,因此,该操作运行retrieve's your data,但由于您没有在OpenAsync中挂起任何内容,因此可能会假定操作数已在第一个实例上完成,然后继续,然后加载数据,因此在第二次尝试时,您就有了要处理的数据,因为在第一次尝试时已经填充了该数据
所以我想看更多的代码
然而,我要说的一件事是.Wait()是不好的如果你必须等待结果并强制等待,那么更好的方法是.getwaiter().GetResult()
我可以为你链接一个详细解释这一点的研讨会。但本质上,.Wait()
会将异常抛出到空白中,并使它们非常难以跟踪(或者至少比您希望的要困难得多)
“请注意,我在异步/等待方面绝对不是专家,请随时纠正我”感谢您的回复。我发布的代码是我拥有的复制该问题的最小代码量。我可以编辑我的原始帖子,并在其中添加额外的层,看看是否有帮助。我编辑了我的原始帖子,添加了我在实际应用程序中进行的每个呼叫。上一次我忽略了其中的大部分内容,因为我能够使用精简版本重现问题,但这可能会提供一些见解。