web-dev-qa-db-ja.com

ユーザーフィードバックを含むASP.NET MVC長時間実行プロセスが必要

かなり複雑なレポートになる可能性があるものを配信するために、プロジェクトにコントローラーを作成しようとしています。その結果、比較的長い時間がかかる可能性があり、プログレスバーは確かにユーザーが物事が進行していることを知るのに役立ちます。レポートはAJAXリクエストを介して開始されます。定期的なJSONリクエストがステータスを取得し、進行状況バーを更新するという考えです。

AsyncControllerを試してみましたが、これはリソースを占有せずに長いプロセスを実行する良い方法のようですが、進行状況を確認する方法を提供していないようです(さらに、JSONリクエストをブロックし、まだ理由はわかりません)。その後、コントローラーの静的変数に進行状況を保存し、そこからステータスを読み取る方法を試しましたが、正直に言って、すべてが少しハックに思えます!

すべての提案は感謝して受け入れられました!

48
Jason

ここに私があなたが試すことができると書いたサンプルがあります:

コントローラ:

public class HomeController : AsyncController
{
    public ActionResult Index()
    {
        return View();
    }

    public void SomeTaskAsync(int id)
    {
        AsyncManager.OutstandingOperations.Increment();
        Task.Factory.StartNew(taskId =>
        {
            for (int i = 0; i < 100; i++)
            {
                Thread.Sleep(200);
                HttpContext.Application["task" + taskId] = i;
            }
            var result = "result";
            AsyncManager.OutstandingOperations.Decrement();
            AsyncManager.Parameters["result"] = result;
            return result;
        }, id);
    }

    public ActionResult SomeTaskCompleted(string result)
    {
        return Content(result, "text/plain");
    }

    public ActionResult SomeTaskProgress(int id)
    {
        return Json(new
        {
            Progress = HttpContext.Application["task" + id]
        }, JsonRequestBehavior.AllowGet);
    }
}

Index()ビュー:

<script type="text/javascript">
$(function () {
    var taskId = 543;
    $.get('/home/sometask', { id: taskId }, function (result) {
        window.clearInterval(intervalId);
        $('#result').html(result);
    });

    var intervalId = window.setInterval(function () {
        $.getJSON('/home/sometaskprogress', { id: taskId }, function (json) {
            $('#progress').html(json.Progress + '%');
        });
    }, 5000);
});
</script>

<div id="progress"></div>
<div id="result"></div>

アイデアは、HttpContext.Applicationを使用して進行状況を報告する非同期操作を開始することです。つまり、各タスクには一意のIDが必要です。次に、クライアント側でタスクを開始してから、複数のAJAX=リクエスト(5秒ごと)を送信して進捗状況を更新します。パラメーターを微調整してシナリオに合わせることができます。さらに改善を加えると、例外処理。

51
Darin Dimitrov

この質問に回答してから4.5年後、このタスクをはるかに簡単に行えるライブラリ、SignalRがあります。共有状態を使用する必要はありません(予期しない結果につながる可能性があるため、これは悪いことです)、HubContextクラスを使用して、クライアントにメッセージを送信するハブに接続します。

最初に、通常のようにSignalR接続をセットアップします(例: here を参照)。ただし、ハブにサーバー側のメソッドは必要ありません。次に、AJAXを呼び出してEndpoint/Controller/whateverを呼び出し、通常どおり取得する接続IDを渡します:var connectionId = $.connection.hub.id;。サーバー側では、別のスレッドでプロセスを開始し、クライアントに200 OKを返すことができます。プロセスはconnectionIdを認識しているため、次のようにメッセージをクライアントに送信できます。

GlobalHost.ConnectionManager.GetHubContext<LogHub>()
                              .Clients.Client(connectionId)
                              .log(message);

ここで、logはサーバーから呼び出すクライアント側のメソッドであるため、SignalRで通常行うように定義する必要があります。

$.connection.logHub.client.log = function(message){...};

詳細は私のブログ投稿 here

6
ulu

選択肢としてWebサーバーマシンへのアクセス権がある場合は、Windowsサービスまたはシンプルなコンソールアプリケーションを作成して、長時間実行する作業を実行できます。 Webアプリは、操作を開始することを示すためにレコードをdbに追加する必要があり、dbの新しいレコードを定期的にチェックするWindowsサービスは、タスクの実行を開始し、dbに進行状況を報告する必要があります。 Webアプリはajaxリクエストを使用して進行状況を確認し、ユーザーに表示できます。

この方法を使用して、asp.net mvcアプリケーションのExcelレポートを実装しました。このレポートは、マシンで実行され、レポートテーブルの新しいレコードを常にチェックするWindowsサービスによって作成され、新しいレコードが見つかると、レポートの作成を開始し、レコードフィールドを更新して進行状況を示します。 Asp.net mvcアプリケーションは、新しいレポートレコードを追加し、データベースが終了するまで進行状況を追跡し、ダウンロードの準備ができているファイルへのリンクを提供しました。

0
Dovlet Mamenov