2

MVC2 で AsyncController を使用する方法を学習しようとしていますが、ドキュメントやチュートリアルはほとんどありません。サードパーティ サービスへのエクスポートが非常に遅い通常のコントローラー メソッドを 1 つ取り、それを非同期メソッドに変換しようとしています。

元のコントローラーメソッド:

public JsonResult SaveSalesInvoice(SalesInvoice invoice)
{
    SaveInvoiceToDatabase(invoice); // this is very quick 
    ExportTo3rdParty(invoice); // this is very slow and should be async
}

そこで、AsyncController を継承する新しいコントローラーを作成しました。

public class BackgroundController : AsyncController
{
    public void ExportAysnc(int id)
    {
        SalesInvoice invoice = _salesService.GetById(id);
        ExportTo3rdParty(invoice);
    }

    public void ExportCompleted(int id)
    {
         // I dont care about the return value right now, 
         // because the ExportTo3rdParty() method
         // logs the result to a table
    }

    public void Hello(int id)
    {            
    }
}

次に、jQuery から Export メソッドを呼び出します。

function Export() {
    $.post("Background/Export", { id: $("#Id").val() }, function (data) {
    // nothing to do yet
    });
}

しかし、結果は 404 not found エラーです (Background/Export が見つかりません)。Background/Hello または Background/ExportAysnc を呼び出そうとすると、それらが見つかります。

私は何を間違っていますか?

4

1 に答える 1

8

実際には2つのユースケースがあります

  1. 長時間の操作の結果が気になる
  2. あなたは結果を気にしません(火をつけて忘れてください)

最初のケースから始めましょう:

public class BackgroundController : AsyncController
{
    public void ExportAysnc(int id)
    {
        AsyncManager.OutstandingOperations.Increment();

        Task.Factory.StartNew(() => DoLengthyOperation(id));

        // Remark: if you don't use .NET 4.0 and the TPL 
        // you could manually start a new thread to do the job
    }

    public ActionResult ExportCompleted(SomeResult result)
    {
        return Json(result, JsonRequestBehavior.AllowGet);
    }

    private void DoLengthyOperation(int id)
    {
        // TODO: Make sure you handle exceptions here
        // and ensure that you always call the AsyncManager.OutstandingOperations.Decrement()
        // method at the end
        SalesInvoice invoice = _salesService.GetById(id);
        AsyncManager.Parameters["result"] = ExportTo3rdParty(invoice);
        AsyncManager.OutstandingOperations.Decrement();
    }
}

これで、次のように呼び出すことができます。

$.getJSON(
    '<%= Url.Action("Export", "Background") %>', 
    { id: $("#Id").val() }, 
    function (data) {
        // do something with the results
    }
);

Web サービス呼び出しについて言及したので、Web サービスのクライアント プロキシを生成したときに、非同期メソッド (XXXCompleted および XXXAsync) を発行する機会があったことを意味します。

public class BackgroundController : AsyncController
{
    public void ExportAysnc(int id)
    {
        AsyncManager.OutstandingOperations.Increment();
        // that's the web service client proxy that should
        // contain the async versions of the methods
        var someService = new SomeService();
        someService.ExportTo3rdPartyCompleted += (sender, e) =>
        {
            // TODO: Make sure you handle exceptions here
            // and ensure that you always call the AsyncManager.OutstandingOperations.Decrement()
            // method at the end

            AsyncManager.Parameters["result"] = e.Value;
            AsyncManager.OutstandingOperations.Decrement();
        };
        var invoice = _salesService.GetById(id);
        someService.ExportTo3rdPartyAsync(invoice);
    }

    public ActionResult ExportCompleted(SomeResult result)
    {
        return Json(result, JsonRequestBehavior.AllowGet);
    }
}

これは、I/O 完了ポートに依存し、長時間の操作の実行中にサーバー上のスレッドを独占しないため、非同期コントローラーの可能な限り最良の使用方法です。


2 番目のケースは簡単です (実際には非同期コントローラーは必要ありません)。

public class BackgroundController : Controller
{
    public ActionResult Export(int id)
    {
        // Fire and forget some lengthy operation
        Task.Factory.StartNew(() => DoLengthyOperation(id));
        // return immediately
        return Json(new { success = true }, JsonRequestBehavior.AllowGet);
    }
}

これは、Async コントローラーに関する MSDNの素晴らしい記事です。

于 2011-02-20T08:59:19.380 に答える