繁体   English   中英

Xamarin.Forms ViewModel 中的异步方法不等待 AzureServiceTokenProvider 和 SqlConnection 初始化

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM