2009-08-08 2 views
8

У меня есть приложение ASP.NET MVC, которое в настоящее время использует класс WebClient для простого вызова внешнего веб-сервиса из действия контроллера.Использование WebClient в ASP.NET MVC асинхронно?

В настоящее время я использую метод DownloadString, который выполняется синхронно. Я столкнулся с проблемами, когда внешняя веб-служба не отвечает на запросы, что приводит к тому, что мое приложение ASP.NET не истощается и не реагирует на запросы.

Каков наилучший способ устранить эту проблему? Существует метод DownloadStringAsync, но я не уверен, как вызвать это из контроллера. Нужно ли использовать класс AsyncController? Если да, то как взаимодействует метод AsyncController и DownloadStringAsync?

Спасибо за помощь.

ответ

7

Я думаю, что использование AsyncControllers поможет вам здесь, поскольку они выгружают обработку из потока запросов.

Я хотел бы использовать что-то вроде этого (с использованием шаблона событий, как описано в this article):

public class MyAsyncController : AsyncController 
{ 
    // The async framework will call this first when it matches the route 
    public void MyAction() 
    { 
     // Set a default value for our result param 
     // (will be passed to the MyActionCompleted method below) 
     AsyncManager.Parameters["webClientResult"] = "error"; 
     // Indicate that we're performing an operation we want to offload 
     AsyncManager.OutstandingOperations.Increment(); 

     var client = new WebClient(); 
     client.DownloadStringCompleted += (s, e) => 
     { 
      if (!e.Cancelled && e.Error == null) 
      { 
       // We were successful, set the result 
       AsyncManager.Parameters["webClientResult"] = e.Result; 
      } 
      // Indicate that we've completed the offloaded operation 
      AsyncManager.OutstandingOperations.Decrement(); 
     }; 
     // Actually start the download 
     client.DownloadStringAsync(new Uri("http://www.apple.com")); 
    } 

    // This will be called when the outstanding operation(s) have completed 
    public ActionResult MyActionCompleted(string webClientResult) 
    { 
     ViewData["result"] = webClientResult; 
     return View(); 
    } 
} 

И убедитесь, что вы установили все, что маршруты вам нужно, например, (в Global.asax.cs):

public class MvcApplication : System.Web.HttpApplication 
{ 
    public static void RegisterRoutes(RouteCollection routes) 
    { 
     routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); 

     routes.MapAsyncRoute(
      "Default", 
      "{controller}/{action}/{id}", 
      new { controller = "Home", action = "Index", id = "" } 
     ); 
    } 
} 
+0

есть ли способ добавить тайм-аут к асинхронному вызову webclient? –

+1

Просто примечание, похоже, этот ответ был опубликован в 09 в MVC 1.0 дня. Теперь с MVC 2/3 ответ СЛУЧАЙНО отличается. Метод MapAsyncRoute ушел и больше не нужен. Кроме того, метод MyAction теперь должен быть переименован в MyActionAsync.В противном случае все работает одинаково. – BFree

3

Метод DownloadStringAsync использует модель события, поднимая DownloadStringCompleted по завершении. Вы также можете остановить запрос, если он слишком длинный, вызывая WebClient.CancelAsync(). Это позволит вашему основному потоку запросов и потоку WebClient работать параллельно и позволит вам точно определить, сколько времени вы хотите, чтобы ваш основной поток подождал, прежде чем вернуться.

В приведенном ниже примере мы запускаем загрузку и устанавливаем обработчик событий, который мы хотим вызвать, когда он будет завершен. DownloadStringAsync немедленно возвращается, поэтому мы можем продолжить обработку остальной части нашего запроса.

Чтобы продемонстрировать более подробный контроль над этой операцией, когда мы достигнем конца действия нашего контроллера, мы можем проверить, завершена ли загрузка; если нет, дайте ему еще 3 секунды, а затем отмените.

string downloadString = null; 

ActionResult MyAction() 
{ 
    //get the download location 
    WebClient client = StartDownload(uri); 
    //do other stuff 
    CheckAndFinalizeDownload(client); 
    client.Dispose(); 
} 

WebClient StartDownload(Uri uri) 
{ 
    WebClient client = new WebClient(); 
    client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(Download_Completed); 
    client.DownloadStringAsync(uri); 
    return client; 
} 

void CheckAndFinalizeDownload(WebClient client) 
{ 
    if(this.downloadString == null) 
    { 
     Thread.Sleep(3000); 
    } 
    if(this.downloadString == null) 
    { 
     client.CancelAsync(); 
     this.downloadString = string.Empty; 
    } 
} 

void Download_Completed(object sender, DownloadStringCompletedEventArgs e) 
{ 
    if(!e.Cancelled && e.Error == null) 
    { 
     this.downloadString = (string)e.Result; 
    } 
} 
+0

Я считаю, что этот подход все равно будет блокировать поток запросов, хотя вы все еще находитесь в потоке запроса во время вызова сна. Асинхронные контроллеры помогут здесь, потому что они освобождают поток запросов, пока вы ждете внешнего ресурса. –

+0

@ Майкл действительно. Этот подход будет выполняться параллельно с потоком запросов, если запрос WebClient не будет завершен к тому моменту, когда поток контроллера выполнит все остальное, что ему нужно сделать - в этот момент контроллер выбирает, следует ли блокировать/спать или продолжать. У меня не сложилось впечатление, что вопрос о том, как использовать AsyncController, конкретно связан с тем, как заставить WebClient работать асинхронно. Это может быть неправильное чтение вопроса. –

+0

@Rex Да, я думаю, из описания веб-приложения, которое «нитевидно голодает и не отвечает», что, скорее всего, у OP заканчиваются потоки запросов. Ограничение внешнего ответа на 3 секунды поможет немного (если ваши внешние запросы ранее занимали больше времени), но это еще довольно долгое время, чтобы поддерживать поток, и, следовательно, он не будет масштабироваться. –

 Смежные вопросы

  • Нет связанных вопросов^_^