0

MVCアプリケーション内で特定のフォームのPDFコピーを生成しようとしています。これは時間がかかり、クライアントがこの世代が起こるのを待つ必要はないので、私はこれを一連の火災および忘れ忘れの仕事として引き起こそうとしています。MVCコントローラから起動するとTask.Runがサイレントに失敗することがある

HttpContextを確立する必要があること、または変更できないコードの一部が機能しないことに注意してください。私はこの問題に対処していると信じていますが、重要な場合にはそれを呼び出すことにしました。ここで

は、私は、そのファイルへのパスを渡し、htmlファイルに問題のMVCのフォームのHTMLコンテンツの文字列出力を書いています、の中で私は...

private void AsyncPDFFormGeneration(string htmlOutput, string serverRelativePath, string serverURL, string signature, ScannedDocument document, HttpContext httpContext) 
    { 
     try 
     { 
      System.Web.HttpContext.Current = httpContext; 
      using (StreamWriter stw = new StreamWriter(Server.MapPath(serverRelativePath), false, System.Text.Encoding.Default)) 
      { 
       stw.Write(htmlOutput); 
      } 

      Doc ABCDoc = new Doc(); 
      ABCDoc.HtmlOptions.Engine = EngineType.Gecko; 
      int DocID = 0; 
      DocID = ABCDoc.AddImageUrl(serverURL + serverRelativePath + "?dumb=" + DateTime.Now.Hour.ToString() + DateTime.Now.Minute.ToString() + DateTime.Now.Second + DateTime.Now.Millisecond); 
      while (true) 
      { 
       ABCDoc.FrameRect(); 
       if (!ABCDoc.Chainable(DocID)) 
        break; 
       ABCDoc.TextStyle.LeftMargin = 100; 
       ABCDoc.Page = ABCDoc.AddPage(); 
       DocID = ABCDoc.AddImageToChain(DocID); 
      }//End while (true... 

      for (int i = 1; i <= ABCDoc.PageCount; i++) 
      { 
       ABCDoc.PageNumber = i; 
       ABCDoc.Flatten(); 
      } 

      ScannedDocuments.AddScannedDocument(document, ABCDoc.GetData()); 

      System.IO.File.Delete(Server.MapPath(serverRelativePath)); 
     } 
     catch (Exception e) 
     { 
      //Exception is logged to the database, and if that fails, to the Event Log 
     } 
    } 

を呼び出しています機能ですPDFライターに送信し、PDFを生成して、htmlファイルを削除します。

ので、同様に、私は、コントローラのPOSTメソッドの中にそれを呼んでいる:

Task.Run(() => AsyncPDFFormGeneration(htmlOutput, serverRelativePath, 
    serverURL, signature, document, HttpContext.ApplicationInstance.Context)); 

このコマンドは、フォームを作成し、foreachループの一部として呼ばれる文字列形式にそれらをロードし、通過しますそれらをタスクに変換します。また、私は念のために奇妙な何かがTask.Runで起こっていたが、それは異なる結果を生じなかった

Task.Factory.StartNew 

でこれを試してみました。

問題は、すべてのタスクが毎回実行されるわけではありません。私がVisual Studioで実行し、デバッグを進めていくと、毎回正しく動作します。しかし、11フォームを連続して生成しようとすると、時には3または4が生成され、時には1を除いてすべて生成されることもあります。

エラーログは可能な限り大きく設定されていますが、私が見つけることができる例外がスローされ、生成されたhtmlファイルは、途中で中断されたスレッドのために私のファイル構造の中に横たわって残されません。

ページがポストからどれくらい早く戻ってくるか、生成されるフォームの数には若干の相関があるようです。より長い読み込み時間は、一般に生成されるフォームのより多くに関連しています...しかし、私は重要ではない印象の下にいました。これらのスレッドを分離して、HttpContextの独自のコピーで分割して持ち運びます。起動したら、元のスレッドがそれらに影響するとは思わなかった。

なぜ私はいくつかの試行で3つの成功したタスクを取得していますが、別の試行では11個も例外もありません。

+3

https://blog.stephencleary.com/2014/06/fire-and-forget-on-asp-net.html現在、「ThreadPool」アプローチを行っています。 –

+0

@ScottChamberlainこれはアプリケーションのリサイクルに関係しているようです。私の問題は、毎回、これらのフォームを生成するページを送信し、それらのフォームの乱数を生成しています。 ThreadPoolのアプローチは、クライアントからの要求に応答した後、個々の要求ハンドラスレッドがシャットダウンするのと同じ問題がありますか? – guildsbounty

+0

スレッドは、ASP.NETスレッドプールのデフォルトの動作であるため、中断されています。安定したサービスを使用して作業を待ち行列に入れてください.Windowsでdireclyを実行しないでください。 – VMAtm

答えて

0

すべてのタスクが完了する前に、アプリケーションが終了している可能性があるので、await Task.WhenAll(task1, task2, task3, etc)を試してみます。

+0

これは、FireとForgetタスクを使用する目的を打ち消しませんか?私は、ユーザーにビューを返す前に、タスクが完了するのを待っていません。 PDFをバックグラウンドで生成し続ける一方で、ユーザーが他のものに移行できるようにしたい。 – guildsbounty

+1

@guildsbounty火をつけてそのように忘れることはできません。従来の分散処理技術(メッセージキュー)やHangfireなどを使用します。 – Crowcoder

0
Task.Run(() => AsyncPDFFormGeneration(htmlOutput, serverRelativePath, 
serverURL, signature, document, HttpContext.ApplicationInstance.Context)); 

この行には微妙な競合状態があります。問題はHttpContext.ApplicationInstance.Contextプロパティにあります。タスクの開始時に評価されます。それが要求の終わりの前に起こるなら、これは問題ありません。しかし、何らかの理由でタスクの開始に少し時間がかかる場合、要求は最初に完了し、HttpContextはnullになります。したがって、null参照例外が発生し、タスクが開始しなかったという印象を与えます(実際にはtry/catchのすぐ外でクラッシュしたとき)。

ちょうどローカル変数にコンテキストを格納し、Task.Runのためにそれを使用し、それを避けるために:

var context = HttpContext; // Or HttpContext.ApplicationInstance.Context, but I don't really see the point 
Task.Run(() => AsyncPDFFormGeneration(htmlOutput, serverRelativePath, serverURL, signature, document, context)); 

私はrequres System.Web.HttpContext.Currentを設定することをあなたが使用しているAPIを知らない、と述べました、それは火災と忘れの仕事のための非常に悪い選択と思われる。 HttpContextをローカルに保存しても、まだ整理されているので、期待どおりに動作するかどうかはわかりません。

また、コメントに記載されているように、ASP.NETでFire-and-forgetタスクを起動することは危険です。代わりにHostingEnvironment.QueueBackgroundWorkItemを使用してください。

関連する問題