Tengo el siguiente código de prueba WebAPI, no uso WebAPI en producción, pero hice esto debido a una discusión que tuve sobre esta pregunta: Pregunta asíncrona de WebAPI
De todos modos, aquí está el método WebAPI ofensivo:
public async Task<string> Get(int id)
{
var x = HttpContext.Current;
if (x == null)
{
// not thrown
throw new ArgumentException("HttpContext.Current is null");
}
await Task.Run(() => { Task.Delay(500); id = 3; });
x = HttpContext.Current;
if (x == null)
{
// thrown
throw new ArgumentException("HttpContext.Current is null");
}
return "value";
}
Hasta ahora, había creído que se esperaba la segunda excepción porque cuando se await
complete, es probable que esté en un hilo diferente donde, HttpContext.Current
como una variable estática de hilo, ya no se resolverá en el valor apropiado. Ahora, según el contexto de sincronización, en realidad podría verse obligado a volver al mismo hilo después de la espera, pero no estoy haciendo nada elegante en mi prueba. Este es un simple e ingenuo uso de await
.
En los comentarios de otra pregunta me dijeron que HttpContext.Current
debería resolverse después de una espera. Incluso hay otro comentario sobre esta pregunta que indica lo mismo. Entonces, ¿qué es verdad? ¿Debería resolverse? Creo que no, pero quiero una respuesta autorizada sobre esto porque async
y await
es lo suficientemente nuevo como para no encontrar nada definitivo.
TL; DR: ¿Es HttpContext.Current
potencialmente null
después de un await
?
fuente
AspNetSynchronizationContext
que se ocupaHttpContext
, noawait
. Además, la devolución de llamada de continuación paraawait
puede (y muy probablemente ocurrirá) en un hilo diferente para el modelo de ejecución de API web..ConfigureAwait(false)
algún momento. No hay una solicitud ni un controlador que se entregue explícitamente a través de la capa empresarial, ya que esa no es compatible con la web. Esto es útil, por ejemplo, para un módulo de registro que puede inyectar los detalles de la solicitud cuando la lógica empresarial escribe un genéricoTraceInformation
.Respuestas:
Asegúrese de escribir una aplicación ASP.NET 4.5 y apuntar a 4.5.
async
yawait
tienen un comportamiento indefinido en ASP.NET a menos que esté ejecutando en 4.5 y esté usando el nuevo contexto de sincronización "fácil de usar".En particular, esto significa que debe:
httpRuntime.targetFramework
en4.5
, oappSettings
, establezcaaspnet:UseTaskFriendlySynchronizationContext
entrue
.Más información está disponible aquí .
fuente
<httpRuntime targetFramework="4.5" />
es lo que lo solucionó, gracias por aclarar.targetFramework
en 4.5.1 o 4.5, pero async en 4.5.1 debería funcionar bien.Como @StephenCleary señaló correctamente, necesita esto en su web.config:
<httpRuntime targetFramework="4.5" />
Cuando solucioné este problema por primera vez, hice una búsqueda en toda la solución de lo anterior, confirmé que estaba presente en todos mis proyectos web y lo descarté rápidamente como el culpable. Finalmente se me ocurrió mirar esos resultados de búsqueda en contexto completo:
<!-- For a description of web.config changes for .NET 4.5 see http://go.microsoft.com/fwlink/?LinkId=235367. The following attributes can be set on the <httpRuntime> tag. <system.Web> <httpRuntime targetFramework="4.5" /> </system.Web> -->
Doh.
Lección: Si actualiza un proyecto web a la versión 4.5, aún debe implementar esa configuración manualmente.
fuente
Su prueba no tiene fallas y HttpContext.Current no debe ser nulo después de la espera porque en ASP.NET Web API cuando espera, esto asegurará que el código que sigue a esta espera se pase el HttpContext correcto que estaba presente antes de la espera.
fuente
Me encontré con este problema recientemente. Como señaló Stephen, no establecer explícitamente el marco objetivo puede generar este problema.
En mi caso, nuestra API web se migró a la versión 4.6.2, pero el marco de destino de tiempo de ejecución nunca se especificó en la configuración web, por lo que básicamente faltaba dentro de la etiqueta <system.web>:
Si tiene dudas sobre la versión del marco que está ejecutando, esto puede ayudar: agregue la siguiente línea en cualquiera de sus métodos de API web y establezca un punto de interrupción para verificar qué tipo está cargado actualmente en tiempo de ejecución y verificar que no sea una implementación heredada:
Debería ver esto (AspNetSynchronizationContext):
En lugar de LegazyAspNetSynchronizationContext (que fue lo que vi antes de agregar el marco de destino):
Si va al código fuente ( https://referencesource.microsoft.com/#system.web/LegacyAspNetSynchronizationContext.cs ) verá que la implementación heredada de esta interfaz carece de soporte asincrónico.
Pasé mucho tiempo tratando de encontrar la fuente del problema y la respuesta de Stephen ayudó mucho. Espero que esta respuesta proporcione más información sobre el problema.
fuente