Есть ли способ обрабатывать async/ждать службы ASMX?

У меня есть веб-приложение, обслуживающее API-интерфейс WCF для JSON и веб-службы ASMX. Приложение существует уже несколько лет. Он основан на ASP.NET 2.0, но пару лет назад был обновлен до .NET 4.0, и я просто обновился до .NET 4.5, чтобы иметь возможность использовать новую инфраструктуру async.

За приложением находятся некоторые устаревшие службы, и я понял, что существует большой потенциал для повышения производительности, перейдя на асинхронный. Я реализовал async полностью через приложение, и все отлично работает с API-интерфейсом WCF.

Слишком поздно я обнаружил, что API ASMX терпит неудачу, мне нужны такие методы:

[WebMethod(Description = "Takes an internal trip ID as parameter.")]
async public Task<Trip> GetTrip(int tripid)
{
    var t = await Trip.GetTrip(tripid);
    return t;
}

Затем я узнал, что async/await вообще не поддерживается в ASMX, и все советуют мигрировать в WCF. Я не слишком радостен об этом. ASMX (на самом деле три из них) заполнены различными методами, и есть масса потребителей API, которые мы хотим продолжать обслуживать со старого API.

Но нам нужна повышенная производительность! Кто-нибудь знает об обходном пути, поэтому я могу продолжать использовать async/await за ASMX, но выставлять ASMX как раньше?

Ответ 1

Возможно, это будет возможно, но это будет немного неудобно. ASMX поддерживает асинхронные методы в стиле APM, и вы можете конвертировать TAP в APM (однако обратите внимание, что пример MSDN на этой странице не распространяется правильно на исключения).

У меня есть пример в моем блоге, в котором показано как завершать реализации TAP в APM (с распространением исключения, которое сохраняет правильный тип исключения, но теряет стек, см. ExceptionDispatchInfo для полного правильного распространения исключений). Я использовал это некоторое время, когда WCF поддерживал только APM. Для ASMX должен работать очень похожий подход.

Однако обратите внимание, что должно быть нацелено на 4.5 (т.е. httpRuntime.targetFramework) для async/await для работы как ожидалось.

Ответ 2

Возможно, вы не сможете заставить сам сервис ASMX вести себя асинхронно, но если вы звоните из WebApp с использованием javascript, вы можете обернуть обещание вокруг сервиса при вызове, чтобы воспользоваться асинхронностью на стороне клиента.

Как уже говорилось, он не делает вашу службу асинхронной, но является примером использования этой службы асинхронным способом в клиенте (т.е. обещание javascript используется в этом случае, чтобы остановить дальнейшую работу до тех пор, пока служба не будет разрешена. Точно так же он может быть настроен на продолжение работы и выполнение других действий после устранения или сбоя).

Оптимизация самого сервиса - это только половина решения, другая половина находится внутри клиента, поэтому я подумал, что для некоторых это будет полезным расширением :)

Код Javascript:

var tripObj;

async function getTripObject(tripId) {


  // Ensures Trip is fetched before proceeding 
  var promise = new Promise(function (resolve, reject) {
    ASMXService.getTrip(tripId, async function (ret) {
      tripObj = ret;

      resolve("Success");
    }, function () {
      reject("Error");
    });
  });

  await promise;

}

Ответ 3

Если ваша проблема заключается в простом интеграции вершин вашей асинхронной логики в вашем asmx, вы можете сделать следующий фрагмент.

[WebMethod(Description = "Takes an internal trip ID as parameter.")] 
public Trip GetTrip(int tripid) {
   var trip = Trip.GetTrip(tripid).Wait();
   return trip; 
}

Помните, что если Trip.GetTrip() выдает исключение, вы получите исключение AggregateException, а не исключение, которое вы получили бы, если бы оно ожидало выброса исключения. Вы можете сделать

[WebMethod(Description = "Takes an internal trip ID as parameter.")] 
public Trip GetTrip(int tripid) {
  try
  {
    var trip = Trip.GetTrip(tripid).Wait();
    return trip; 
  }
  catch(AggregateException ex)
  {
    throw ex.InnerException.First();
  }       
}