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

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

(编辑以添加有关我打电话的更多详细信息)

我有一个Xamarin表单应用程序连接到Azure App Services中托管的.Net Core 2.2 web服务

在我的视图模型中,我有这样一个调用:

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()
会将异常抛出到空白中,并使它们非常难以跟踪(或者至少比您希望的要困难得多)


“请注意,我在异步/等待方面绝对不是专家,请随时纠正我”

感谢您的回复。我发布的代码是我拥有的复制该问题的最小代码量。我可以编辑我的原始帖子,并在其中添加额外的层,看看是否有帮助。我编辑了我的原始帖子,添加了我在实际应用程序中进行的每个呼叫。上一次我忽略了其中的大部分内容,因为我能够使用精简版本重现问题,但这可能会提供一些见解。