0

長いサーバー側のプロセスにWebインターフェイスを配置しようとしています。このプロセスは、プロセスの実行中に定期的な進行状況/統計レポートをクライアントに送信する必要があります。これどうやってするの?

これが私がこれまでに試みたことです。ループが処理されている限り、webmethodのセッションはnullです。ループが終了し、スタートボタンをもう一度押すと、セッション値を取得してラベルにデータを入力できます。プロセスの実行中にこれを取得してクライアントに更新を送信するにはどうすればよいですか?

VS2012とASP.NET4.5を使用しています。

編集:より具体的には、サーバーがループでビジー状態のときに問題が発生します。ループを取り除いて、サーバーから定期的に変数値を取得しようとすると、問題はありません。その変数をループに入れて、定期的にフェッチしてみてください。問題が何であるかがわかります。私が投稿したコードは、実行すると問題が明確になるはずです。

ありがとう。

Default.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="ClientProgressTest.Default" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
    <script type="text/javascript">
        function GetCount() {

        $.ajax({
            type: "POST",
            url: "Default.aspx/GetCount",
            contentType: "application/json; charset=utf-8",
            data: {},
            dataType: "json",
            success: function (data) {
                lblCount.innerHTML = data.d;
            },
            error: function (result) {
                alert(result.status + ' ' + result.statusText);
            }
        });

        setTimeout(GetCount, 5000);
    }
</script>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <label id="lblCount"></label>
        <br />
        <br />
        <asp:Button ID="btnGetCount" runat="server" Text="Get Count" OnClientClick="GetCount();" OnClick="btnGetCount_Click" />
    </div>
    </form>
</body>
</html>

Default.aspx.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace ClientProgressTest
{
    public partial class Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {

        }

        [WebMethod(EnableSession = true)]
        public static string GetCount()
        {
            string count = null;

            if (HttpContext.Current.Session["count"] != null)
            {
                count = HttpContext.Current.Session["count"].ToString();
            }

            return count;
        }

        protected void btnGetCount_Click(object sender, EventArgs e)
        {
            Session["count"] = null;

            for (int i = 0; i < 10000000; i++)
            {
                Session["count"] = i;
            }
        }
}
}
4

4 に答える 4

1

私はここにこの質問に対する長い答えを置きます:ajaxリクエスト内のステータスラベルを更新する方法

他の人が同じ解決策を見つけることができるように、私もここに答えを貼り付けます:

================================================== ================================

この例では、AJAXにJQueryを使用しており、コードビハインドはvb.netにあります。

基本的に、最初の呼び出しを行って長いプロセスを開始し、次に2番目のメソッドを使用して2番目の呼び出しを繰り返し、長いプロセスのステータスを取得する必要があります。

AJAX

これは、長いプロセスへの主な呼びかけです。必要に応じて、データをメソッドに渡す必要があります。processNameがあることに注意してください。このプロセスのステータスのみを取得できるように、これはランダムな種類の文字列である必要があります。他のユーザーは別のランダムなprocessNameを持つため、混乱したステータスになることはありません。

JAVASCRIPT

    var processName = function GenerateProcessName() {

        var str = "";
        var alhpabet = "abcdefghijklmnopqrstuvwxyz";
        for (i = 1; i < 20; i++) {
            str += alhpabet.charAt(Math.floor(Math.random() * alhpabet.length + 1));
        }
        return str;
    }


    function StartMainProcess(){
    $j.ajax({
            type: "POST",
            url: "/MyWebservice.asmx/MyMainWorker",
            data: "{'processName' : '" + processName + "','someOtherData':'fooBar'}",
            contentType: "application/json; charset=utf-8",
            dataType: "json",
            success: function (msg) {
                if (msg.d) {                        
                    // do a final timerPoll - process checker will clear everything else
                    TimerPoll();
                }
            }
        });
        TimerPoll();
     }

2番目のAJAX呼び出しは、進行状況を取得するための別のメソッドへの呼び出しになります。これは、タイマー方式によってXXX回ごとに呼び出されます。

これはTimerPoll関数です。この場合、3秒ごとに起動します

JAVASCRIPT

function TimerPoll() {
        timer = setTimeout("GetProgress()", 3000)
    }

そして最後に、GetProgress()関数を使用して、進行状況を取得します。このユーザーの呼び出しのみのプロセスを取得するには、上記で使用したものと同じprocessNameを渡す必要があります

JAVASCRIPT

function GetProgress() {
        // make the ajax call
        $j.ajax({
            type: "POST",
            url: "/MyWebService.asmx/MyMainWorkerProgress",
            data: "{'processName' : '" + processName + "'}",
            contentType: "application/json; charset=utf-8",
            dataType: "json",
            success: function (msg) {

                // Evaulate result..
                var process = msg.d

                if (process.processComplete) {
                    // destroy the timer to stop polling for progress
                    clearTimeout(timer);

            // Do your final message to the user here.                      

                } else {

                   // show the messages you have to the user.
                   // these will be in msg.d.messages

                    // poll timer for another trip
                    TimerPoll();
                }
        });

    }

これで、バックエンドに、AJAXが通信するいくつかのWebメソッドがあります。また、すべての進行状況情報と、ユーザーに返したいものを保持するための共有/静的オブジェクトも必要になります。

私の場合、MyMainWorkerProcessを呼び出すたびに、プロパティが入力されて返されるクラスを作成しました。これは少しこのように見えます。

VB.NET

    Public Class ProcessData
        Public Property processComplete As Boolean
        Public Property messages As List(Of String) = New List(Of String)
    End Class

このクラスを使用する共有プロパティもあります。これは次のようになります...(この共有プロパティは、複数のユーザーによる複数のプロセスの進行状況を保持できます。したがって、辞書です。辞書のキーはプロセス名になります。すべての進行状況データはクラスProcessDataに含まれる

Private Shared processProgress As Dictionary(Of String, ProcessData) = New Dictionary(Of String, ProcessData)

私のメインワーカー関数は少しこのように見えます。最初に、同じプロセスが別のprocessProgressにないことを確認します。

VB.NET

<WebMethod()> _
<ScriptMethod(ResponseFormat:=ResponseFormat.Json)> _
Public Function MyMainWorker(ByVal processName as string, ByVal SomeOtherData as string) as Boolean

        '' Create progress object - GUI outputs process to user
        '' If the same process name already exists - destroy it
        If (FileMaker.processProgress.ContainsKey(processName)) Then
            FileMaker.processProgress.Remove(processName)
        End If

        '' now create a new process
        dim processD as ProcessData = new ProcessData() with {.processComplete = false}


        '' Start doing your long process.

        '' While it's running and after whatever steps you choose you can add messages into the processData which will be output to the user when they call for the updates
         processD.messages.Add("I just started the process")

         processD.messages.Add("I just did step 1 of 20")

         processD.messages.Add("I just did step 2 of 20 etc etc")

         '' Once done, return true so that the AJAX call to this method knows we're done..
        return true

End Function

あとは、progressメソッドを呼び出すだけです。これから行うのは、前に設定したのと同じprocessNameを持つディクショナリprocessDataを返すことだけです。

VB.NET

<WebMethod()> _
    <ScriptMethod(ResponseFormat:=ResponseFormat.Json)> _
    Public Function MyMainWorkerProgress(ByVal processName As String) As ProcessData

        Dim serializer As New JavaScriptSerializer()

        If (FileMaker.processProgress.ContainsKey(processName)) Then
            Return processProgress.Item(processName)
        Else
            Return New ProcessData()
        End If

    End Function

そして出来上がり..

要約すると..

  1. 2つのWebメソッドを作成します。1つは長いプロセスを実行し、もう1つは進行状況を返します。
  2. これらのWebメソッドへの2つの別々の呼び出しを作成します。1つ目はメインワーカー、2つ目はxx秒間繰り返され、進行状況を示すものです。
  3. 適切と思われる方法でユーザーにメッセージを出力します...

いくつかのタイプミスがある場合は私を撃たないでください:)

于 2012-12-05T14:12:15.420 に答える
1

典型的なWebアプリケーションフレームワークは、この種のものにはあまり適していません。つまり、これはASP.Netの欠点だけではなく、PHP、RoR、および将来的には他の多くのフレームワークで発生する可能性があります。

長時間実行されるタスクは、IISの外部に存在するプロセスで発生させる必要があります。Webページの提供以外の何かでWebサーバーをダウンさせる理由はありません。これを行うには、Webアプリケーションコード内からプロセスを生成するか、バックグラウンドで常に実行されているサービスを作成し、いくつかの共通リソース(データベースなど)でジョブをチェックします。

いずれの場合も、共通リソースが進行状況インジケーターとして機能します。PercentCompleteタスクが進むにつれて、外部プロセスは継続的に値を更新します。一方、Webアプリケーションには、この値をチェックしてクライアントに返すための何らかのメソッドが必要になります。AJAX Webメソッドを使用すると、おそらく最良の結果が得られます。

于 2012-12-05T13:59:59.897 に答える
1

プロセスの現在の進行状況を返すWebサービスを作成します。クライアントスクリプトでタイマーを設定します。このタイマーは、起動時にWebサービスから現在の進行状況を取得し、クライアント側の表示要素を新しい値で動的に更新します。

于 2012-12-05T14:01:00.623 に答える
0

おそらく最も最新で楽しい実装方法(Webサーバープロセスの外部でそれを実行していない場合)は、SignalRハブを使用してメッセージをクライアントに中継することです。こちらのgitハブでチェックしてくださいhttps://github.com/SignalR/SignalR

于 2012-12-05T14:00:41.323 に答える