web-dev-qa-db-ja.com

MVCコントローラーのアクションでの非同期/待機

ASP.net MVCコントローラーにIndexアクションがあります。このアクションは、(特に)行の大きなセットを含むSQLテーブルでカウントを行うプライベートアクションを呼び出します。返された番号はビューバッグプロパティに挿入されます。

public ActionResult Index() 
{
    // do things
    ViewBag.NumberOfRows = NumberOfRows();
    return View();
}

private string NumberOfRows()
{
    // sql connection and row count
    return numberOfRows;
}

これは機能しますが、行数を含めてeverythingが実行されるまで、インデックスページは表示されません。代わりに、プライベート関数がまだ完了していない場合でも、Indexアクションをすぐに完了します。カウントが完了したら、ビューバッグプロパティに値を設定します。今私はこれをやった:

private async Task<string> NumberOfRows()
{
    SqlConnection connection = new SqlConnection(connString);
    SqlCommand cmd = new SqlCommand();
    SqlDataReader reader;

    cmd.CommandText = "SELECT SUM (row_count) FROM sys.dm_db_partition_stats WHERE object_id=OBJECT_ID('aTable') AND (index_id=0 or index_id=1)";
    cmd.CommandType = CommandType.Text;
    cmd.Connection = connection;

    await connection.OpenAsync();

    reader = await cmd.ExecuteReaderAsync();
    string numberOfRows = "N/A";
    while (await reader.ReadAsync())
    {
        numberOfRows = reader.GetInt64(0).ToString();
    }

    connection.Close();

    return numberOfRows ;
}

public async Task<ActionResult> Index(FormCollection form){
    // do things;
    ViewBag.NumberOfRows = await NumberOfRows();
    return View();
}

これは機能します。しかし、これは本当に非同期なのでしょうか?私は何かを逃していますか、これを行う他の方法はありますか?

13
BAD_SEED

async呼び出しですが、ここで理解しておくべき重要なことの1つは、コントローラーのアクションをasyncにした場合です。スレッド(asp.netスレッドプールの)スレッドは、要求を処理してスレッドプール(asp .netリクエストスレッドプール)。

つまり、adプールのスレッドを解放して、より多くの要求を処理します(非同期コントローラーアクションは、より多くの要求を処理するのに役立つだけであり、処理時間を短縮することを意味するのではなく、サーバーの応答性を高めるだけです)。 async/awaitでの操作が完了すると、要求スレッドプールからの新しいスレッドがさらに処理を行います。

実際の非同期ページが必要な場合、つまりページの応答性を高めたい場合は、jQueryの.ajax()関数を使用するか、Asp.net MVCで利用可能なajax拡張機能を使用することをお勧めします。

15
Pranay Rana

これは機能します。しかし、これは本当に非同期なのでしょうか?

データベースにクエリを実行すると(IOバインドされた操作))、ASP.NET Thread-Poolスレッドを使用して、クエリが完了するまでブロックする代わりに解放するので、非同期です。 。

非同期は"この要求を呼び出し元に返し、後で実行を終了します"を意味するわけではありません。これは、HTTP要求/応答プロトコルを壊しません。あなたが望むものは非同期では達成されません。

リクエストをすぐに完了する場合は、バックグラウンドスレッドでリクエストをキューに入れ、操作が完了したらデータをクライアント側にプッシュする必要があります。

7
Yuval Itzchakov

非同期の代わりに、Task.Runを使用します。

Task.Run(() => MethodToExecuteInBackground(methodParameters)).ConfigureAwait(false).GetAwaiter();

コントローラのIndexメソッドからasync修飾子を削除する必要もあります。

これで、コントローラーメソッドは、MethodToExecuteInBackgroundがオフになり、バックグラウンドでいくつかの作業を行う間、すぐに戻ります。

これにより、MethodToExecuteInBackgroundが終了するまでスレッドプールのスレッドが拘束されることに注意してください。

0
jjxtra