ASP.NET MVC 内で WebClient を非同期的に使用しますか?
-
12-09-2019 - |
質問
現在、WebClient クラスを使用して、コントローラー アクション内から外部 Web サービスへの単純な呼び出しを行う ASP.NET MVC アプリケーションがあります。
現在、同期的に実行される DownloadString メソッドを使用しています。外部 Web サービスが応答しないという問題に遭遇しました。その結果、ASP.NET アプリケーション全体がスレッド不足になり、応答しなくなります。
この問題を解決する最善の方法は何ですか?DownloadStringAsync メソッドがありますが、コントローラーからそれを呼び出す方法がわかりません。AsyncController クラスを使用する必要がありますか?その場合、AsyncController と DownloadStringAsync メソッドはどのように相互作用しますか?
助けてくれてありがとう。
解決
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 = "" }
);
}
}
他のヒント
DownloadStringAsync メソッドはイベント モデルを使用し、 ダウンロード文字列完了 それが終わったとき。時間がかかりすぎる場合は、呼び出してリクエストを停止することもできます。 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;
}
}