2017-06-07 19 views
0

私は何百ものチェックボックスとドロップダウンメニューを持っています(それらの多くは一緒に結合されています)。このアクションでは、Session内のオブジェクトを更新する更新メカニズムがあります。このオブジェクトはすべての検証と値の結合を行います。たとえば、ユーザーが1つの入力フィールドに%50と入力した場合、3つの新しいSelectListItemをドロップダウンに追加できます。新しいリクエストがASP.NET MVC 5に到着したときに古いリクエスト処理を中止するにはどうすればよいですか?

すべてが正常に動作しますが、使用するとすぐにチェックボックスをクリックするようになると、コントローラは以前のものを処理している間に複数の投稿を取得します。幸いにも私たちは最後のPOSTにしか興味がありませんので、同じフォームからのより新しいリクエストが来たときに、リクエストをキャンセルする方法が必要です。

私が試したもの: 1つ前のサーバでサーバが動作しているときにクライアントサイドが複数の投稿を作成するのをブロックします。ブラウザ側で目立つポーズが発生するため好ましくありません。

2ハッシュコードまたはAntiForgeryTokenを使用して複数のポストバックをブロックする方法はいくつかあります。しかし、彼らは私が必要とするものではありません、私は新しいリクエストに有利な進行中のスレッドを中止し、着信要求をブロックしないようにする必要があります。

3ハッシュコード(またはAntiForgeryToken)を保持するために2つのメッセージハンドラ(アクション前とアクション後)を追加してパイプラインを拡張しようとしましたが、問題はまだあります。スレッドは同じリクエストを処理しているので、そのスレッドを中止するか、古いリクエストを完了するように設定する方法はありません。

どのような考えですか?

+0

クライアント側のソリューションを実装すると、コントローラにポストバックする必要がなくなります。 SPAは、ユーザーが保存したときや他のトリガーが発生したときにオブジェクト状態全体をポストバックできます。 – tawman

+0

C#からJavascriptに2400行以上のコードを移動することはできません。 C#では検証とデータ結合を含むすべてのロジックがあります。 また、このソリューションはSPAではなく、この場合はSPAのように見えます。 – Keivan

+0

私は素敵なアプローチは、バウンスシステムを構築することです、あなたはコマンドの配列を持っているので、チェックボックスが変更されるたびにこの操作を保存し、毎秒実行するタイムアウト機能を持っていると想像してください、この関数は、要求を実行してクリアします。このようにして、最後の操作で1分間に1つの要求が発生します –

答えて

0

ご協力ありがとうございます@ xavier-j。 これを回った後、私はこれを書きました。同じことを必要とする誰かのために役立つことを願って:あなたは、私がキートークンとして、ここでAntiForgeryTokenを使用しています。このActionFilterは

public class KeepLastRequestAttribute : ActionFilterAttribute 
{ 
    public string HashCode { get; set; } 
    public override void OnActionExecuting(ActionExecutingContext filterContext) 
    { 
     base.OnActionExecuting(filterContext); 
     Dictionary<string, CancellationTokenSource> clt; 
     if (filterContext.HttpContext.Application["CancellationTokensDictionary"] != null) 
     { 
      clt = (Dictionary<string, CancellationTokenSource>)filterContext.HttpContext.Application["CancellationTokensDictionary"]; 
     } 
     else 
     { 
      clt = new Dictionary<string, CancellationTokenSource>(); 
     } 
     if (filterContext.HttpContext.Request.Form["__RequestVerificationToken"] != null) 
     { 
      HashCode = filterContext.HttpContext.Request.Form["__RequestVerificationToken"]; 
     } 

     CancellationTokenSource oldCt = null; 
     clt.TryGetValue(HashCode, out oldCt); 
     CancellationTokenSource ct = new CancellationTokenSource(); 
     if (oldCt != null) 
     { 
      oldCt.Cancel(); 
      clt[HashCode] = ct; 
     } 
     else 
     { 
      clt.Add(HashCode, ct); 
     } 

     filterContext.HttpContext.Application["CancellationTokensDictionary"] = clt; 
     filterContext.Controller.ViewBag.CancellationToken = ct; 
    } 

    public override void OnResultExecuted(ResultExecutedContext filterContext) 
    { 
     base.OnResultExecuted(filterContext); 
     if (filterContext.Controller.ViewBag.ThreadHasBeenCanceld == null && filterContext.HttpContext.Application["CancellationTokensDictionary"] != null) { 
      lock (filterContext.HttpContext.Application["CancellationTokensDictionary"]) 
      { 
       Dictionary<string, CancellationTokenSource> clt = (Dictionary<string, CancellationTokenSource>)filterContext.HttpContext.Application["CancellationTokensDictionary"]; 
       clt.Remove(HashCode); 
       filterContext.HttpContext.Application["CancellationTokensDictionary"] = clt; 
      } 
     } 
    } 
} 

を追加必要

まず、あなたはより多くのコントロールを持っている独自のカスタムハッシュコードを追加することができます。

は、コントローラでは、あなたが現在進行中の要求を中止する必要があなたのJavaScriptでこのような何か

 [HttpPost] 
    [KeepLastRequest] 
    public async Task<ActionResult> DoSlowJob(CancellationToken ct) 
    { 
     CancellationTokenSource ctv = ViewBag.CancellationToken; 
     CancellationTokenSource nct = CancellationTokenSource.CreateLinkedTokenSource(ct, ctv.Token, Response.ClientDisconnectedToken); 

     var mt = Task.Run(() => 
     { 
      SlowJob(nct.Token); 
     }, nct.Token); 
     await mt; 

     return null; 
    } 

    private void SlowJob(CancellationToken ct) 
    { 
     for (int i = 0; i < 10; i++) 
     { 
      Thread.Sleep(200); 
      if (ct.IsCancellationRequested) 
      { 
       this.ViewBag.ThreadHasBeenCanceld = true; 
       System.Diagnostics.Debug.WriteLine("cancelled!!!"); 
       break; 
      } 
      System.Diagnostics.Debug.WriteLine("doing job " + (i + 1)); 
     } 
     System.Diagnostics.Debug.WriteLine("job done"); 

     return; 
    } 

そして最後に、そうでない場合は、ブラウザのブロック新しい要求を持つことになります。

 var onSomethingChanged = function() { 

     if (currentRequest != null) { 
      currentRequest.abort(); 
     } 

     var fullData = $('#my-heavy-form :input').serializeArray(); 

     currentRequest = $.post('/MyController/DoSlowJob', fullData).done(function (data) { 
      // Do whatever you want with returned data 
     }).fail(function (f) { 
      console.log(f); 
     }); 
     currentRequest.always(function() { 
      currentRequest = null; 
     }) 
    } 
0

唯一のものは、クライアント側のリクエストを抑制します。基本的には、チェックボックスをクリックするとタイムアウトを設定する必要があります。その最初の要求を通過させることができますが、それ以降の要求はキューに入れられます(またはシナリオで最初にキューに入れられた要求の後に実際にドロップされます)。

サーバーサイドのリクエストを中止する方法はありません。各要求は冪等である。それ以前に起きたことやそれ以後に起きたことについての固有の知識はありません。サーバーには複数のスレッドがあり、要求を処理し、できるだけ速く処理します。リクエストの処理方法やレスポンスの送信方法はありません。最初の要求は、各要求の処理がどのように行われるかによって、応答を受け取る3番目の要求になります。

0

非同期テクノロジでトランザクション機能を実装しようとしています(つまり、最後のリクエストのみをカウントしています)。これは設計上の欠陥です。

クライアント側でブロックすることを拒否するため、最初に処理する要求を制御する方法や、クライアント側で結果を正しく処理する方法がありません。

あなたが実際にこのシナリオに遭遇するかもしれません:

  1. クライアントは
  2. Serverは
  3. クライアントがリクエスト・B
  4. Serverは要求Bに
  5. サーバーが返すの処理を開始する送信リクエストBの処理を開始する要求を送信要求Bの結果とそれに応じたクライアントの変更
  6. サーバーは要求Aとクライアントの結果を返しますそれに応じて変更されます(リクエストBに起因する以前の変更を元に戻す)

正しい順序を保証するには、ブロッキングが唯一の方法です。

関連する問題