crear - wcf iis 10



Uso del AspNetSynchronizationContext de WCF alojado en IIS (1)

Estoy alojando mis servicios .NET 4.5 WCF en IIS. Hay una información llamada "BusinessContext" (BC) que se almacena en la instancia de OperationContext.Current, de modo que cualquier lógica en sentido descendente pueda alcanzarla.

Todo funcionó bien hasta que presenté async / await, y me encontré con este problema . @ stephen-cleary mencionó que ASP.NET usa AspNetSynchronizationContext, que es amigable con los archivos, para mantener el HttpContext.Current entre los hilos. Como estoy alojando en IIS, pensé que debería poder aprovechar AspNetSyncCtx en WCF y usar HttpContext.Current en lugar de OperationContext para almacenar el BC.

Creé un servicio WCF desde cero, que tiene targetFramework = 4.5, aspnet: UseTaskFriendlySynchronizationContext = true y aspNetCompatibilityEnabled = true establecido de forma predeterminada en Web.config. También agregué AspNetCompatibilityRequirements = Obligatorio a mi servicio.

En tiempo de ejecución, veo el HttpContext.Current está allí, pero SynchronizationContext.Current es nulo. Después de esperar, HttpContext pasa a ser nulo, lo que se espera porque no hay SyncCtx. ¿No debería configurarse en AspNetSyncCtx cuando se requiere aspcompatibilidad? ¿Cómo se establece AspNetSyncCtx en ASP.NET?

-- Solución posible.

Siguiendo a Stephen-cleary aquí, seguí adelante y definí un SynchronizationContext personalizado para preservar el OperationContext entre los hilos.

Me gustaría escuchar las opiniones de la comunidad con respecto a esta implementación. Gracias.

public class OperationContextSynchronizationContext : SynchronizationContext
{
    public override void Post(SendOrPostCallback d, object state)
    {
        OperationContext opCtx = OperationContext.Current;
        InternalState internalState = new InternalState()
        {
            OpCtx = opCtx,
            Callback = d,
            State = state,
            SyncCtx = this
        };
        ThreadPool.QueueUserWorkItem(new WaitCallback(InternalInvoker), internalState);
    }
    private void InternalInvoker(object internalState)
    {
        InternalState internalSt = internalState as InternalState;
        SynchronizationContext.SetSynchronizationContext(internalSt.SyncCtx);
        using (new OperationContextScope(internalSt.OpCtx))
        {
            internalSt.Callback.Invoke(internalSt.State);
        }
    }
    private class InternalState
    {
        public SynchronizationContext SyncCtx { get; set; }
        public OperationContext OpCtx { get; set; }
        public SendOrPostCallback Callback { get; set; }
        public object State { get; set; }
    }
}

Answer #1

Me encontré con el problema (¿error?) Que mencionó que HttpContext.Current no fluía con el código async en un servicio WCF alojado en IIS incluso con aspNetCompatiblity habilitado y requerido.

Logré que mi proyecto funcionara utilizando LogicalCallContext con CallContext.LogicalSetData y CallContext.LogicalGetData tal como lo describe Stephen Cleary en esta publicación del blog . Creo que establecer / obtener su contexto comercial de esta manera funcionaría muy bien en su caso y podría ser más ligero y conceptualmente más simple que el SynchronizationContext personalizado. En su caso, debe establecer el contexto de su empresa en algún lugar de todos modos ... podría también configurarlo en LogicalCallContext y creo que todo estará bien.

Explicaré mi 'solución' personal con más detalle, aunque admito que es un poco hacky ya que estoy moviendo manualmente todo el objeto HttpContext (y solo en métodos WCF). Pero, de nuevo, su caso con su objeto de contexto personalizado parecería ser una mejor opción.

En la aplicación web ASP.NET que heredé, hubo llamadas a HttpContext.Current repartidas por toda la lógica de negocios (no ideal). Obviamente, el proyecto funcionó bien hasta que quise que varios de los métodos de WCF en la aplicación fueran async .

Muchas de las llamadas en la lógica de negocios serían de cargas de páginas ASP.NET, etc., donde todo funciona bien tal como es.

Resolví (¿arruinaron?) Mi problema en .NET 4.5 creando una pequeña clase de ayuda ContextHelper y reemplacé todas las llamadas a HttpContext.Current con ContextHelper.CurrentHttpContext .

public class ContextHelper
{
    public static void PrepareHttpContextFlow()
    {
        CallContext.LogicalSetData("CurrentHttpContext", HttpContext.Current);
    }

    public static HttpContext CurrentHttpContext
    {
        get
        {
            return HttpContext.Current ?? 
                   CallContext.LogicalGetData("CurrentHttpContext") 
                   as HttpContext;
        }
    }
}

Ahora, cada vez que se define HttpContext.Current , mi método de ayuda es esencialmente no operativo.

Para que esto funcione, los puntos de entrada para mis llamadas a WCF deben llamar al método PrepareHttpContextFlow antes de la primera llamada a await que es ciertamente problemático y la razón principal por la que considero que esto es un kludge.

En mi caso, este inconveniente se mitiga por el hecho de que necesito OTRAS llamadas manuales para agregar información de pila lógica para el contexto de registro de errores agregado que se pierde al usar async (ya que la información de la pila física en una excepción no es muy útil para los registros de errores en este caso). Así que al menos si recuerdo hacer una de las llamadas de "preparación para asincronización", debería recordar hacer lo siguiente :)





async-await