简体   繁体   English

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

[英]Async method in Xamarin.Forms ViewModel not waiting for AzureServiceTokenProvider and SqlConnection to initialize

(Edited to add more detail about every call I'm making) (编辑以添加有关我正在拨打的每个电话的更多详细信息)

I have a Xamarin Forms application connecting to a .Net Core 2.2 web service hosted in Azure App Services.我有一个 Xamarin Forms 应用程序连接到 Azure 应用服务中托管的 .Net Core 2.2 Web 服务。

In my view model I have a call like this:在我的视图模型中,我有一个这样的电话:

private async Task GetItems() {            
    var result = await itemsListFactory.GetItemsAsync()
}

Which calls this:这称为:

public async Task<IEnumerable<IItemInfo>> GetItemsAsync() {
    return await ItemList.GetItemListAsync();
}

Which calls this (CSLA business object):其中调用此(CSLA 业务对象):

  public static async Task<ItemList> GetItemListAsync() {
        return await DataPortal.FetchAsync<ItemList>();
    }

Which calls this:这称为:

[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;
}

Which calls:其中调用:

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;         
}

The implementation of the AzureConnectionManager looks like this: 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;
}

However, the first time I make this call (eg first call of the day, or after not using the service for a while) I get no results back.但是,我第一次拨打此电话时(例如当天的第一次电话,或一段时间未使用该服务后),我没有收到任何结果。 Any subsequent calls seem to work just fine.任何后续调用似乎都可以正常工作。 My guess is this has something to do with the service having to take a few "extra steps" to return data due to inactivity.我的猜测是,这与由于不活动而必须采取一些“额外步骤”来返回数据的服务有关。

This suspicion seems to be confirmed whenever I debug the web service and set breakpoints in my view model as well as the server-side code.每当我调试 Web 服务并在我的视图模型和服务器端代码中设置断点时,这种怀疑似乎都会得到证实。 Whenever the service's call returns with no records it's almost as if it's returning early from the server, because it returns to the view model with no data, and then my debugger hops back onto the server after it's received the access token.每当服务的调用返回而没有记录时,它几乎就像是从服务器提前返回一样,因为它返回到没有数据的视图模型,然后我的调试器在收到访问令牌后跳回服务器。 So, it's as if my code decided not to wait for the GetAccessTokenAsync and OpenAsync to finish what they had to do before returning to the client.因此,就好像我的代码决定不等待 GetAccessTokenAsync 和 OpenAsync 在返回客户端之前完成它们必须执行的操作。

I can fix this by adding a .Result to GetAccessTokenAsync() and .Wait() to OpenAsync() like this:我可以通过将 .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;
        }

But this feels like a hack.但这感觉就像一个黑客。

I doubt this is the way I'm supposed to fix this, but maybe it is.我怀疑这是我应该解决的方法,但也许确实如此。 At the very least I'd like to just understand what's going on here if this is the correct way to handle this situation.如果这是处理这种情况的正确方法,至少我想了解这里发生了什么。

The await operator suspends evaluation of the enclosing async method until the asynchronous operation represented by its operand completes. await 运算符暂停对封闭异步方法的评估,直到由其操作数表示的异步操作完成。 When the asynchronous operation completes, the await operator returns the result of the operation, if any.当异步操作完成时,await 运算符返回操作的结果(如果有)。 When the await operator is applied to the operand that represents already completed operation, it returns the result of the operation immediately without suspension of the enclosing method.当 await 运算符应用于表示已完成操作的操作数时,它会立即返回操作结果,而不会暂停封闭方法。 The await operator doesn't block the thread that evaluates the async method. await 运算符不会阻塞评估异步方法的线程。 When the await operator suspends the enclosing async method, the control returns to the caller of the method.当 await 运算符挂起封闭的异步方法时,控制权返回给方法的调用者。

Official Document on this 关于这个的官方文件

So if we look at the what the documents say about Async/Await you'll notice that因此,如果我们查看文档中关于 Async/Await 的内容,您会注意到

When the await operator is applied to the operand that represents already completed operation, it returns the result of the operation immediately without suspension of the enclosing method.当 await 运算符应用于表示已完成操作的操作数时,它会立即返回操作结果,而不会暂停封闭方法。

More then likely OpenAsync();更有可能OpenAsync(); Is seen as a operand that's already completed as you might not be awaiting your Returns, So the Operation Runs retrieve's your data but because your not suspending anything in OpenAsync It might assume the operand is already completed on the first instance and just continue then the data is loaded so on your second attempt you have the data to work with, as its already populated on the first try.被视为已经完成的操作数,因为您可能没有等待返回,因此操作运行检索您的数据,但因为您没有在 OpenAsync 中挂起任何内容它可能假设操作数已经在第一个实例上完成,然后继续数据已加载,因此在您第二次尝试时您可以使用数据,因为它在第一次尝试时已经填充。

So i'd like to see a bit more code actually.所以我实际上希望看到更多的代码。

However one thing I will say is that .Wait() is Bad If you have to wait for a result and force that wait the better way to do this is .GetAwaiter().GetResult() I can link you a Seminar that explains in details about this.然而,我要说的一件事是 .Wait() 是坏的如果你必须等待结果并强制等待更好的方法是.GetAwaiter().GetResult()我可以链接你一个研讨会,在关于这一点的详细信息。 But in Essence .Wait() Throws exceptions into the void and make them extremly difficult to track(Or at-least far more difficult then you'd want them to be)但是在本质中.Wait()异常抛出到空白中并使它们极难跟踪(或者至少比您希望的要困难得多)

"Also note in no way am I anywhere near a Expert in Async/Await so feel free to correct me" “另请注意,我绝不会靠近 Async/Await 专家,所以请随时纠正我”

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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