У нас есть (внутренний) веб-сервер (IIS), написанный на C# с использованием ASP. NET. Как и большинство веб-сервисов, у нас есть сотни API, все интерфейсы «контроллера», следующие этому шаблону:
class SomeController : System.Web.Http.ApiController
{
[HttpGet] public HttpResponseMessage SomeGetApi(...)
{
try
{
//do stuff
return SomeHttpResponseMessage;
}
finally
{
OurController.Cleanup();
}
}
[HttpPost] public IHttpActionResult SomePostApi(...)
{
try
{
//do something
return SomeHttpActionResult;
}
catch (Exception ex)
{
return SomeErrorHttpActionResult;
}
finally
{
OurController.Cleanup();
}
}
}
Единственная важная деталь здесь - это то, что весь код API обернут как выше на самом внешнем уровне в структуре try-finally, где мы должны вызывать ту же самую процедуру stati c "Cleanup ()", как и последнее, что делается при выходе из API. Другими словами, у нас есть сотни и сотни повторений этого же вызова. Каждый раз, когда кто-то добавляет новый API и забывает try-finally {cleanup ()}, все прерывается.
Очевидно, было бы неплохо, если бы ASP. Net предоставили некоторые механизм вызова некоторого (общего) кода очистки после завершения выполнения каждого API контроллера. Вот варианты, которые я рассмотрел и почему они терпят неудачу:
В ApiController имеется перезаписываемая виртуальная protected virtual void Dispose(bool disposing);
. Это бесполезно для вышеуказанного требования, так как ASP. Net повторно использует экземпляр SomeController, который он создает, и не вызывает Dispose (удаление bool) в конце каждого выполнения API. Это наиболее разумно и совсем не удивительно.
HttpContext
имеет крючок DisposeOnPipelineCompleted(callbackObject)
, который выглядел многообещающе. К сожалению, для наших целей это серьезный недостаток: он вызывается в потоке, отличном от того, в котором выполнялся API, и потоки API используются повторно. Поскольку вызов callbackObject.Dispose()
, таким образом, асинхронен с потоком, который его зарегистрировал, обратный вызов может и часто делает, только начинает выполнение после потока, для которого мы пытаемся выполнить очистку между вызовами API. , начинает выполнение другого вызова API. Другими словами, если API SomePostApi
выполняется в потоке A, и он регистрирует объект обратного вызова очистки CleanupThreadObject
, то после того, как SomePostApi
завершит выполнение (в потоке A), поток A вполне может начать выполнение * 1023. * для какого-то другого пользователя, прежде чем CleanupThreadObject.Dispose()
будет вызван в потоке B, в котором он был запланирован для запуска, даже если регистрационный вызов был сделан из потока A.
Есть ли другие * хуки в * 1038? *. NET, что позволило бы нам регистрировать обратный вызов или выполнять общий код синхронно в конце каждого вызова API контроллера?