Использование WebClient в ASP.NET MVC асинхронно? - PullRequest
8 голосов
/ 08 августа 2009

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

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

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

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

Ответы [ 2 ]

7 голосов
/ 09 августа 2009

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

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

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 = "" }
        );
    }
}
3 голосов
/ 08 августа 2009

Метод 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;
    }
}
...