2009-12-14 10 views
5

Tenemos un C# WebMethod que se llama sincrónicamente por un Delphi CGI (¡no preguntes!). Esto funciona bien, excepto cuando cambiamos a nuestro entorno de recuperación de desastres, que es mucho más lento. El problema es que la solicitud web de Delphi WinInet tiene un tiempo de espera de 30 segundos, que no se puede modificar debido a un error reconocido por Microsoft. En el entorno de recuperación de desastres, C# WebMethod puede demorar más de 30 segundos y el Delphi CGI se cae de bruces.Llamada C# Fire and Forget dentro de un WebMethod

Ahora hemos codificado C# WebMethod para reconocer el entorno en el que se encuentra, y si está en modo de recuperación ante desastres llamamos al método subsiguiente en un hilo y respondemos inmediatamente al CGI para que esté dentro del 30 segundos. Esto tiene sentido en teoría, pero estamos descubriendo que estas llamadas con hilos son erráticas y no se ejecutan el 100% del tiempo. Obtenemos aproximadamente una tasa de éxito del 70%.

Esto es claramente inaceptable y tenemos que conseguirlo al 100%. Los hilos se están llamando con Delegate.BeginInvoke(), que hemos utilizado con éxito en otros contextos, pero no les gusta esto por alguna razón ... obviamente no hay EndInvoke(), porque tenemos que responder de inmediato a el CGI y ese es el final del WebMethod. no

[WebMethod] 
public string NewBusiness(string myParam) 
{ 
    if (InDisasterMode()) 
    { 
     // Thread the standard method call 
     MethodDelegate myMethodDelegate = new MethodDelegate(ProcessNewBusiness); 
     myMethodDelegate.BeginInvoke(myParam, null, null); 
     // Return 'ok' to caller immediately 
     return 'ok'; 
    } 
    else 
    { 
     // Call standard method synchronously to get result 
     return ProcessNewBusiness(myParam); 
    } 
} 

alguna razón que este tipo de 'dispara y olvida' llamada fracasaría si se utiliza en un entorno WebMethod WebService:

Aquí es una versión simplificada del WebMethod? Si es así, ¿hay una alternativa?

Lamentablemente, alterar el lado Delphi no es una opción para nosotros, la solución debe estar en el lado C#.

Cualquier ayuda que pueda proporcionar sería muy apreciada.

+0

Por cierto ... en su ejemplo como lo es ahora, a la que llama el método NUNCA ProcessNewBusiness en el caso "InDisasterMode". (Intente encontrar el nombre del método entre las líneas 5 a 11). –

+0

Buen lugar - He corregido el ejemplo ahora – JamesW

Respuesta

10

¿Intenta utilizar el "HttpContext" en su método? Si es así, primero debe almacenarlo en una variable local ... también, yo tendría solo uso ThreadPool.QueueUserWorkItem.

Ejemplo:

[WebMethod] 
public string NewBusiness(string myParam) 
{ 
    if (InDisasterMode()) 
    { 
     // Only if you actually need this... 
     HttpContext context = HttpContext.Current; 

     // Thread the standard method call 
     ThreadPool.QueueUserWorkItem(delegate 
     { 
      HttpContext.Current = context; 

      ProcessNewBusiness(myParam); 
     }); 

     return 'ok'; 
    } 
    else 
    { 
     // Call standard method synchronously to get result 
     return ProcessNewBusiness(myParam); 
    } 
} 
+0

Me encanta cuando hay una respuesta simple. Esto parece funcionar perfectamente en nuestro servidor de prueba el 100% del tiempo, incluso sin el código HttpContext. La prueba del entorno de desastres en sí tendrá que esperar una ventana adecuada, pero estoy seguro de que la hemos logrado. Gracias por tu ayuda! – JamesW

+0

La prueba en el servidor de recuperación de desastres fue un éxito completo. ¡Gracias de nuevo! – JamesW

+0

Me resulta difícil convertir este método en VB.NET (preferencia del cliente) para .NET 2.0, ¿me puede ayudar? –

0

como dice la documentación, EndInvoke debe ser llamado siempre, así que hay que crear un ayudante para realizar operaciones dispara y olvida como éste: http://www.reflectionit.nl/Blog/default.aspx?guid=ec2011f9-7e8a-4d7d-8507-84837480092f

me pega el código:

public class AsyncHelper { 
delegate void DynamicInvokeShimProc(Delegate d, object[] args); 

static DynamicInvokeShimProc dynamicInvokeShim = new 
    DynamicInvokeShimProc(DynamicInvokeShim); 

static AsyncCallback dynamicInvokeDone = new 
    AsyncCallback(DynamicInvokeDone); 

    public static void FireAndForget(Delegate d, params object[] args) { 
    dynamicInvokeShim.BeginInvoke(d, args, dynamicInvokeDone, null); 
    } 

    static void DynamicInvokeShim(Delegate d, object[] args) { 
    DynamicInvoke(args); 
    } 

    static void DynamicInvokeDone(IAsyncResult ar) { 
    dynamicInvokeShim.EndInvoke(ar); 
} 
} 

Utilizamos este código es exitoso en nuestra aplicación, aunque no es web.

+0

Realmente no necesitas construir tu propia clase "AsyncHelper" ... solo usa ThreadPool.QueueUserWorkItem –

+0

Gracias por la sugerencia jmservera - muy apreciada, pero encontré el uso de ThreadPool una solución mucho más simple. – JamesW

+0

De nada. La solución de Timothy Khouri también es más rápida. – jmservera