[英]Async method in Xamarin.Forms ViewModel not waiting for AzureServiceTokenProvider and SqlConnection to initialize
(编辑以添加有关我正在拨打的每个电话的更多详细信息)
我有一个 Xamarin Forms 应用程序连接到 Azure 应用服务中托管的 .Net Core 2.2 Web 服务。
在我的视图模型中,我有一个这样的电话:
private async Task GetItems() {
var result = await itemsListFactory.GetItemsAsync()
}
这称为:
public async Task<IEnumerable<IItemInfo>> GetItemsAsync() {
return await ItemList.GetItemListAsync();
}
其中调用此(CSLA 业务对象):
public static async Task<ItemList> GetItemListAsync() {
return await DataPortal.FetchAsync<ItemList>();
}
这称为:
[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 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;
}
AzureConnectionManager 的实现如下所示:
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;
}
但是,我第一次拨打此电话时(例如当天的第一次电话,或一段时间未使用该服务后),我没有收到任何结果。 任何后续调用似乎都可以正常工作。 我的猜测是,这与由于不活动而必须采取一些“额外步骤”来返回数据的服务有关。
每当我调试 Web 服务并在我的视图模型和服务器端代码中设置断点时,这种怀疑似乎都会得到证实。 每当服务的调用返回而没有记录时,它几乎就像是从服务器提前返回一样,因为它返回到没有数据的视图模型,然后我的调试器在收到访问令牌后跳回服务器。 因此,就好像我的代码决定不等待 GetAccessTokenAsync 和 OpenAsync 在返回客户端之前完成它们必须执行的操作。
我可以通过将 .Result 添加到 GetAccessTokenAsync() 和 .Wait() 到 OpenAsync() 来解决这个问题,如下所示:
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;
}
但这感觉就像一个黑客。
我怀疑这是我应该解决的方法,但也许确实如此。 如果这是处理这种情况的正确方法,至少我想了解这里发生了什么。
await 运算符暂停对封闭异步方法的评估,直到由其操作数表示的异步操作完成。 当异步操作完成时,await 运算符返回操作的结果(如果有)。 当 await 运算符应用于表示已完成操作的操作数时,它会立即返回操作结果,而不会暂停封闭方法。 await 运算符不会阻塞评估异步方法的线程。 当 await 运算符挂起封闭的异步方法时,控制权返回给方法的调用者。
因此,如果我们查看文档中关于 Async/Await 的内容,您会注意到
当 await 运算符应用于表示已完成操作的操作数时,它会立即返回操作结果,而不会暂停封闭方法。
更有可能OpenAsync();
被视为已经完成的操作数,因为您可能没有等待返回,因此操作运行检索您的数据,但因为您没有在 OpenAsync 中挂起任何内容它可能假设操作数已经在第一个实例上完成,然后继续数据已加载,因此在您第二次尝试时您可以使用数据,因为它在第一次尝试时已经填充。
所以我实际上希望看到更多的代码。
然而,我要说的一件事是 .Wait() 是坏的如果你必须等待结果并强制等待更好的方法是.GetAwaiter().GetResult()
我可以链接你一个研讨会,在关于这一点的详细信息。 但是在本质中.Wait()
异常抛出到空白中并使它们极难跟踪(或者至少比您希望的要困难得多)
“另请注意,我绝不会靠近 Async/Await 专家,所以请随时纠正我”
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.