Asyn c метод в Xamarin.Forms ViewModel не ожидает инициализации AzureServiceTokenProvider и SqlConnection - PullRequest
0 голосов
/ 12 февраля 2020

(отредактировано для добавления более подробной информации о каждом звонке, который я совершаю)

У меня есть приложение Xamarin Forms, подключающееся к веб-службе. Net Core 2.2, размещенной в Azure Службы приложений.

В моей модели представления у меня есть вызов, подобный этому:

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

Однако, в первый раз, когда я звоню (например, первый звонок дня или после того, как какое-то время не пользовался услугой), я не получаю никаких результатов. Любые последующие звонки, кажется, работают просто отлично. Я полагаю, что это как-то связано с тем, что службе требуется выполнить несколько «дополнительных шагов» для возврата данных из-за неактивности.

Такое подозрение подтверждается всякий раз, когда я отлаживаю веб-службу и устанавливаю контрольные точки в моем посмотреть модель, а также код на стороне сервера. Всякий раз, когда вызов службы возвращается без записей, это почти как если бы он рано возвращался с сервера, потому что он возвращается к модели представления без данных, и затем мой отладчик возвращается на сервер после получения токена доступа. Таким образом, мой код решил не ждать, пока GetAccessTokenAsyn c и OpenAsyn c завершат sh, что они должны были сделать, прежде чем вернуться к клиенту.

Я могу это исправить, добавив a. Результат для GetAccessTokenAsyn c () и .Wait () для OpenAsyn c () примерно так:

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

Но это похоже на взлом.

Я сомневаюсь в этом это способ, которым я должен это исправить, но, возможно, это так. По крайней мере, я хотел бы просто понять, что здесь происходит, если это правильный способ справиться с этой ситуацией.

1 Ответ

0 голосов
/ 12 февраля 2020

Оператор await приостанавливает оценку включающего асинхронного метода c, пока асинхронная операция, представленная его операндом, не завершится. Когда асинхронная операция завершается, оператор await возвращает результат операции, если таковой имеется. Когда оператор ожидающего применяется к операнду, который представляет уже завершенную операцию, он возвращает результат операции немедленно, без приостановки метода включения. Оператор await не блокирует поток, который оценивает метод asyn c. Когда оператор await приостанавливает включающий асинхронный метод c, элемент управления возвращается к вызывающему методу.

Официальный документ для этого

Так что если мы посмотрим на то, что в документах говорится об Async / Await, и вы заметите, что

Когда к операнду, который представляет уже завершенную операцию, применяется оператор await, он сразу же возвращает результат операции без Приостановка метода включения что-нибудь в OpenAsyn c Это может означать, что операнд уже завершен в первом экземпляре и просто продолжить, а затем данные загружаются, поэтому со второй попытки у вас есть данные для работы, так как они уже заполнены с первой попытки.

Так что я бы хотел увидеть немного больше кода на самом деле.

Однако я скажу одну вещь: .Wait () равен Плохо Если вам нужно ждать результата и заставить его ждать, лучший способ сделать это - .GetAwaiter().GetResult() Я могу связать вас семинар, который подробно объясняет об этом. Но в сущности .Wait() бросает исключения в пустоту и делает их чрезвычайно сложными для отслеживания (или, по крайней мере, гораздо более сложными, чем вы хотели бы, чтобы они были)

"Также обратите внимание, что я нигде рядом с экспертом в Async / Await, так что не стесняйтесь меня поправлять "

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...