2009-12-14 13 views
5

Abbiamo un WebMethod C# che viene chiamato in modo sincrono da un CGI Delphi (non chiedere!). Funziona bene, tranne quando passiamo al nostro ambiente di disaster recovery, che funziona molto più lentamente. Il problema è che la richiesta web WinInet di Delphi ha un timeout di 30 secondi, che non può essere modificato a causa di un bug riconosciuto da Microsoft. Nell'ambiente di disaster recovery, C# WebMethod può impiegare più di 30 secondi e il CGI Delphi cade in superficie.C# Fire and Forget call in a WebMethod

Abbiamo ora codificato il WebMethod C# per riconoscere l'ambiente in cui si trova e, se è in modalità di ripristino di emergenza, chiamiamo il metodo successivo in un thread e rispondiamo immediatamente al CGI in modo che rientri nei 30 secondi. Questo ha senso in teoria, ma stiamo scoprendo che queste chiamate a thread sono irregolari e non vengono eseguite il 100% delle volte. Otteniamo un tasso di successo del 70%.

Questo è chiaramente inaccettabile e dobbiamo arrivare al 100%. I thread vengono chiamati con Delegate.BeginInvoke(), che abbiamo usato con successo in altri contesti, ma a loro non piace per qualche ragione ... ovviamente non c'è EndInvoke(), perché dobbiamo rispondere immediatamente a il CGI e questa è la fine del WebMethod. ci

[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); 
    } 
} 

qualche ragione che questo tipo di 'fire and forget' chiamata fallirebbe se viene utilizzato in un ambiente WebMethod WebService:

Ecco una versione semplificata del WebMethod? Se è così allora c'è un'alternativa?

Sfortunatamente l'alterazione del lato Delphi non è un'opzione per noi - la soluzione deve essere sul lato C#.

Qualsiasi aiuto che potreste fornire sarebbe molto apprezzato.

+0

A proposito ... nel tuo esempio come è ora, non siete mai chiamare il metodo ProcessNewBusiness nel caso "InDisasterMode". (prova a trovare il nome del metodo tra le righe da 5 a 11). –

+0

Buon posto - Ho corretto l'esempio ora – JamesW

risposta

10

Prova a utilizzare "HttpContext" nel metodo? Se è così, dovresti prima memorizzarlo in una variabile locale ... anche io, , basta usare ThreadPool.QueueUserWorkItem.

Esempio:

[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

Mi piace quando c'è una risposta semplice. Questo sembra funzionare perfettamente sul nostro server di prova il 100% delle volte, anche senza il codice HttpContext. Il test per l'ambiente disastro dovrà aspettare una finestra adeguata, ma sono certo che l'abbiamo risolto. Grazie per l'aiuto! – JamesW

+0

Il test sul server Disaster Recovery è stato un successo completo. Grazie ancora! – JamesW

+0

Sto trovando difficile convertire questo metodo in VB.NET (preferenza dei client) per .NET 2.0, puoi aiutare. –

0

Come dice la documentazione, EndInvoke dovrebbe essere sempre chiamato, in modo da avere per creare un aiuto per fare le operazioni FireAndForget come questo: http://www.reflectionit.nl/Blog/default.aspx?guid=ec2011f9-7e8a-4d7d-8507-84837480092f

ho incollare il codice:

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); 
} 
} 

Usiamo questo codice con successo nella nostra applicazione, anche se non è web.

+0

Non hai davvero bisogno di creare la tua classe "AsyncHelper" ... basta usare ThreadPool.QueueUserWorkItem –

+0

Grazie per il suggerimento jmservera - molto apprezzato, ma ho trovato l'uso di ThreadPool una soluzione molto più semplice. – JamesW

+0

Prego. La soluzione di Timothy Khouri è anche più veloce. – jmservera