Как я могу ожидать async-метода без модификатора async в этом родительском методе?

У меня есть метод, который я хочу ждать, но я не хочу вызывать эффект домино, который может вызвать вызов этого метода вызова и ждать его. Например, у меня есть этот метод:

public bool Save(string data)
{
   int rowsAffected = await UpdateDataAsync(data);
   return rowsAffected > 0;
}

Я зову:

public Task<int> UpdateDataAsync()
{
  return Task.Run(() =>
  {
    return Data.Update(); //return an integer of rowsAffected
  }
}

Это не сработает, потому что я должен поставить "async" в сигнатуру метода для Save() а затем я не могу вернуть bool я должен сделать это Task<bool> но я не хочу, чтобы кто-то ожидал Save() метод.

Есть ли способ приостановить выполнение кода, например, ждать или как-то ждать этого кода без модификатора async?

Ответ 1

Как я могу ожидать async-метода без модификатора async в этом родительском методе?

Это похоже на вопрос: "Как я могу написать приложение с помощью С#, но не зависеть от какой-либо среды выполнения.NET?"

Короткий ответ: не делайте этого.

Действительно, то, что вы делаете здесь, - это естественный синхронный метод (Update), что делает его асинхронным, запустив его в потоке пула потоков (UpdateDataAsync), а затем вы хотите заблокировать его, чтобы сделать асинхронный метод появляются синхронно (Save). Серьезные красные флаги.

Я рекомендую вам внимательно изучить знаменитую пару сообщений в блоге Стивена Тууба, чтобы я открывал асинхронные обертки для своих синхронных методов и должен ли я открывать синхронные обертки для моих асинхронных методов. Ответ на оба вопроса - "нет", хотя Стивен Тууб объясняет несколько вариантов сделать это, если вам действительно нужно.

Это "действительно нужно" следует зарезервировать для уровня приложения. Я предполагаю, что эти методы (Update, UpdateDataAsync и Save) находятся в разных слоях приложения (например, данные/служба данных/модель представления). Уровни службы данных/данных не должны выполнять синхронные/асинхронные преобразования. Модель представления (специфичный для приложения) является единственной, у которой есть оправдание для такого преобразования - и она должна делать это только в крайнем случае.

Ответ 2

Изменение: этот ответ был до Task.Run добавлен. С этим дополнительным контекстом сценарий лучше всего описывается как "не делай этого".


Вы можете получить доступ. .Result или использовать .Wait(), но вам нужно знать, как задача выполняется в первую очередь. В частности, вам нужно знать, использует ли он синхронный контекст. Причина, по которой это важно, заключается в том, что если это произойдет, это может зайти в тупик немедленно, потому что некоторым контекстам синхронизации необходимо, чтобы вызывающий контекст полностью вышел (например, MVC sync-context должен оставить метод действия контроллера).

Для защиты от этого сложно, но вы, вероятно, должны всегда явно указывать таймаут с вызовом на .Wait() - на всякий случай.