C # Fire и забыть вызов внутри WebMethod - PullRequest
5 голосов
/ 14 декабря 2009

У нас есть C # WebMethod, который синхронно вызывается CGI в Delphi (не спрашивайте!). Это работает хорошо, за исключением случаев, когда мы переключаемся на нашу среду аварийного восстановления, которая работает намного медленнее. Проблема заключается в том, что веб-запрос Delphi WinInet имеет тайм-аут 30 секунд, который нельзя изменить из-за ошибки, подтвержденной Microsoft. В среде аварийного восстановления WebMethod C # может занимать более 30 секунд, а Delphi CGI падает прямо на лицо.

Теперь мы запрограммировали C # WebMethod для распознавания среды, в которой он находится, и если он находится в режиме аварийного восстановления, мы вызываем последующий метод в потоке и немедленно отвечаем на CGI, чтобы он находился в течение 30 секунд. , Это имеет смысл в теории, но мы находим, что эти многопоточные вызовы ошибочны и не выполняются 100% времени. Мы достигаем 70% успеха.

Это явно неприемлемо, и мы должны довести его до 100%. Потоки вызываются с помощью Delegate.BeginInvoke (), которое мы успешно использовали в других контекстах, но им это почему-то не нравится… очевидно, что EndInvoke () нет, потому что нам нужно немедленно ответить на CGI и это конец 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);
    }
}

Есть ли какая-то причина, по которой такой вызов «запускай и забывай» потерпит неудачу, если его использовать в среде WebService WebMethod? Если так, то есть ли альтернатива?

К сожалению, изменение стороны Delphi для нас не вариант - решение должно быть на стороне C #.

Буду признателен за любую помощь, которую вы сможете оказать.

Ответы [ 2 ]

10 голосов
/ 14 декабря 2009

Вы пытаетесь использовать "HttpContext" в вашем методе? Если это так, вы должны сначала сохранить его в локальной переменной ... также, я бы просто использовал ThreadPool.QueueUserWorkItem .

Пример:

[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 голосов
/ 14 декабря 2009

Как сказано в документации, EndInvoke должен вызываться всегда, поэтому вам нужно создать помощника для выполнения операций FireAndForget, как этот: http://www.reflectionit.nl/Blog/default.aspx?guid=ec2011f9-7e8a-4d7d-8507-84837480092f

Вставляю код:

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

Мы успешно используем этот код в нашем приложении, хотя это не Интернет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...