Можно ли использовать Response.OnStarting с async await? - PullRequest
0 голосов
/ 14 января 2019

Я хотел бы использовать Response.OnStarting для выполнения некоторого кода, который использует шаблон асинхронного ожидания. Метод Response.OnStarting определяется как

 public void OnStarting(Func<Task> callback)

И я часто вижу, что он используется для изменения заголовков с помощью кода, подобного следующему:

 context.Response.OnStarting( () => {
      //modify the headers or do some synchronous work
 });

Однако мне нужно выполнить некоторую асинхронную работу от делегата OnStarting. Допустим, метод, выполняющий асинхронную работу, объявлен следующим образом:

 public Task DoWorkAsync() {

        //write to a db or do something else async

        return Task.CompletedTask;
 }

Допустимо ли вызывать этот метод через делегат OnStarting, используя следующий подход?

  context.Response.OnStarting(async () => {
       await DoWorkAsync();
  });

В Visual Studio это компилируется без предупреждений и, похоже, работает. Однако мне кажется странным, что метод Response.OnStarting может использоваться как для синхронных, так и для асинхронных вызовов. Иногда мир асинхронного ожидания все еще заставляет меня немного почесать голову. Можете ли вы пролить свет на то, работает ли этот подход и почему? И почему Response.OnStarting может использоваться с синхронизированным и асинхронным анонимным методом (если на самом деле это возможно)?

Обновление
Я немного покопался, чтобы посмотреть, смогу ли я найти код, который получает вызовы, передаваемые делегату методу OnStarting.

Я обнаружил, что объект DefaultHttpRespose содержит метод OnStarting и внутри этого метода он в основном вызывает IHttpResponseFeature.OnStarting, см. Здесь:

 public override void OnStarting(Func<object, Task> callback, object state)
    {
        if (callback == null)
        {
            throw new ArgumentNullException(nameof(callback));
        }

        HttpResponseFeature.OnStarting(callback, state);
    }  

ссылка на исходный код

Но, что интересно, единственное место, где я могу найти реализацию IHttpResponseFeature.OnStarting, это здесь с этим кодом:

 void IHttpResponseFeature.OnStarting(Func<object, Task> callback, object state)
    {
        var register = Prop<Action<Action<object>, object>>(OwinConstants.CommonKeys.OnSendingHeaders);
        if (register == null)
        {
            throw new NotSupportedException(OwinConstants.CommonKeys.OnSendingHeaders);
        }

        // Need to block on the callback since we can't change the OWIN signature to be async
        register(s => callback(s).GetAwaiter().GetResult(), state);
    }

Но эта реализация блокирует вызов асинхронного делегата. Есть даже комментарий на этот счет, но я не понимаю, почему это было необходимо? И я не уверен, что это реализация IHttpResponseFeature.OnStarting, которая запускается, она только единственная, которую я смог найти на github. Любая помощь с благодарностью.

1 Ответ

0 голосов
/ 31 января 2019

Однако мне кажется странным, что метод Response.OnStarting может использоваться как для синхронных, так и для асинхронных вызовов.

Синхронные API должны быть реализованы синхронно. A Func<int> должен вернуть int; вместо этого он не может вернуть Task<int>.

Асинхронные API могут быть реализованы асинхронно или синхронно. Func<Task<int>> может иметь асинхронную реализацию, возвращающую Task<int>, которая завершится в будущем. Или он может иметь синхронную реализацию, возвращающую Task<int>, который уже завершен со значением int.

При работе с асинхронными API (включая интерфейсы и подписи делегатов) синхронные реализации полностью допустимы.

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

...