简体   繁体   English

C#Fire和Forget在WebMethod中调用

[英]C# Fire and Forget call inside a WebMethod

We have a C# WebMethod that is called synchronously by a Delphi CGI (don't ask!). 我们有一个由Delphi CGI同步调用的C#WebMethod(不要问!)。 This works fine except when we switch to our disaster recovery environment, which runs a lot slower. 这种方法很好,除非我们切换到运行速度慢得多的灾难恢复环境。 The problem is that the Delphi WinInet web request has a timeout of 30 seconds, which cannot be altered due a Microsoft-acknowledged bug. 问题是Delphi WinInet Web请求的超时时间为30秒,由于Microsoft承认的错误而无法更改。 In the disaster recovery environment, the C# WebMethod can take longer than 30 seconds, and the Delphi CGI falls flat on its face. 在灾难恢复环境中,C#WebMethod可能需要30秒以上的时间,并且Delphi CGI的表面不尽相同。

We have now coded the C# WebMethod to recognise the environment it is in, and if it is in disaster recovery mode then we call the subsequent method in a thread and immediately respond to the CGI so that it is well within the 30 seconds. 我们现在编写了C#WebMethod来识别它所处的环境,如果它处于灾难恢复模式,那么我们在一个线程中调用后续方法并立即响应CGI,以便它在30秒内完成。 This makes sense in theory, but we are finding that these threaded calls are erratic and are not executing 100% of the time. 这在理论上是有道理的,但我们发现这些线程调用是不稳定的,并且不会100%执行。 We get about a 70% success rate. 我们获得了大约70%的成功率。

This is clearly unacceptable and we have to get it to 100%. 这显然是不可接受的,我们必须达到100%。 The threads are being called with Delegate.BeginInvoke(), which we have used successfully in other contexts, but they don't like this for some reason.... there is obviously no EndInvoke(), because we need to respond immediately to the CGI and that's the end of the WebMethod. 使用Delegate.BeginInvoke()调用线程,我们已经在其他上下文中成功使用了它们,但是由于某种原因他们不喜欢这个...显然没有EndInvoke(),因为我们需要立即响应CGI,那是WebMethod的终结。

Here is a simplified version of the WebMethod: 这是WebMethod的简化版本:

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

Is there some reason that this kind of 'fire and forget' call would fail if being used in a WebService WebMethod environment? 是否有某种原因,如果在WebService WebMethod环境中使用这种“即发即忘”调用会失败? If so then is there an alternative? 如果是,那么还有其他选择吗?

Unfortunately altering the Delphi side is not an option for us - the solution must be in the C# side. 不幸的是,改变Delphi方面不是我们的选择 - 解决方案必须在C#方面。

Any help you could provide would be much appreciated. 您可以提供的任何帮助将非常感激。

Do you try to use the "HttpContext" in your method? 您是否尝试在方法中使用“HttpContext”? If so, you should store it in a local variable first... also, I'd just use ThreadPool.QueueUserWorkItem . 如果是这样,你应该首先将它存储在局部变量中......另外,我只使用ThreadPool.QueueUserWorkItem

Example: 例:

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

As the documentation says, EndInvoke should be always called, so you have to create a helper for doing FireAndForget operations like this one: http://www.reflectionit.nl/Blog/default.aspx?guid=ec2011f9-7e8a-4d7d-8507-84837480092f 正如文档所说,应始终调用EndInvoke,因此您必须创建一个帮助程序来执行FireAndForget操作,如下所示: http//www.reflectionit.nl/Blog/default.aspx? guid = ec2011f9-7e8a-4d7d- 8507-84837480092f

I paste the code: 我粘贴代码:

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

We use this code successfully in our application, although it is not web. 我们在我们的应用程序中成功使用此代码,尽管它不是Web。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM