0

AJAX を使用して ASP.NET WebForms ページで実行時間の長いタスク中にステータスの更新を提供したいと考えています。

ScriptManager を取得して、非同期ポストバックと同時に Web サービス要求のスクリプトを実行および処理する方法はありますか?

ページに Web サービス要求を行うスクリプトがあります。ページの読み込み時に実行され、setInterval() を使用して定期的に実行されます。非同期ポストバックが開始される前は正しく実行されていますが、非同期ポストバック中に実行が停止し、非同期ポストバックが完了するまで再び実行されません。

長時間実行タスクを実行する非同期ポストバックをトリガーするボタンを備えた UpdatePanel があります。また、データを取得してページに表示するために正しく動作している AJAX WCF Web サービスのインスタンスもありますが、前述のように、非同期ポストバックが完了するまでデータを取得して表示しません。

非同期ポストバック中に、実行時間の長いタスクが更新をページから Web サービスに送信します。

問題は、Web サービスをデバッグしてステップ実行し、ステータスの更新が正しく設定されていることを確認できますが、非同期ポストバックが完了するまで更新がクライアント スクリプトによって取得されないことです。

スクリプト マネージャーは非同期ポストバックの実行でビジー状態のようです。そのため、ポストバックが完了するまで setInterval() を介して他の JavaScript を実行しません。

スクリプト マネージャーを取得する方法、または非同期ポストバック中にスクリプトを実行して WCF Web サービスからデータを取得する方法はありますか?

PageRequestManager を使用して、非同期ポストバックのクライアント側の BeginRequest イベントでスクリプトを実行するさまざまな方法を試しましたが、スクリプトが実行され、ページ要求の実行中に setInterval() を介して実行する必要があるコードの処理が停止します.

4

4 に答える 4

0

スクリプトマネージャーの後にこれを投げてください。

<script type="text/javascript">
        var prm = Sys.WebForms.PageRequestManager.getInstance();
        prm.add_initializeRequest(InitializeRequestHandler);
        prm.add_endRequest(EndRequestHandler);

        var pbQueue = new Array();
        var argsQueue = new Array();

        function InitializeRequestHandler(sender, args) {
            if (prm.get_isInAsyncPostBack()) {
                args.set_cancel(true);
                pbQueue.push(args.get_postBackElement().id);
                argsQueue.push(document.forms[0].__EVENTARGUMENT.value);
            }
        }

        function EndRequestHandler(sender, args) {
            if (pbQueue.length > 0) {
                __doPostBack(pbQueue.shift(), argsQueue.shift());
            }
        }
    </script>
于 2011-07-15T13:38:00.840 に答える
0

ajax 配管がリクエストをキューに入れている可能性があります。

XHR または jQuery を使用して手動でステータス呼び出しを行ってみてください。これで問題が解決する場合があります。

ただし...一度に発生できる同時リクエストの数には限りがあり、制限に達するとブロッキングが発生し始めることに注意してください。

この制限は、ブラウザー/バージョンによって異なります。

于 2010-05-22T18:51:13.153 に答える
0

将来これを見つける可能性のある人のための解決策のフォローアップ。

さまざまな方法でリクエストを送信してみました:

  • WCF サービスを ServiceReference として ScriptManager に追加するときに作成される AJAX スクリプト オブジェクト。そこで、WCF サービス クラスが GetProgress メソッドを持つ ProgressService の場合、JavaScript で新しい ProgressService オブジェクトを作成し、progressService.GetProgress() を呼び出しました。
  • XmlHttpRequest リクエスト。
  • jQuery $.getJson() リクエスト。
  • Sys.Net.WebRequest リクエスト。
  • Sys.Net.WebServiceProxy 要求。

クライアント要求が送信された場合、つまり ASP.NET ScriptManager によってバッファリングされていない場合でも、IIS の同じ Web サイトの一部である場合、WCF サービスは応答しません。

そこで、完全に別個の WCF プロジェクトと完全に別個の IIS Web サイトを作成するのではなく、従来の ASP.NET (SOAP) Web サービス (.asmx) に切り替えました。

Visual Studio で同じプロジェクトの .asmx サービス部分を保持し、IIS で Web サイトを保持することができました。リクエストはポストバック中に送信され、サービスはポストバック中に応答します。

これを ScriptManager の下に ServiceReference として追加した後、基本的に同じスクリプト オブジェクトを使用して、新しい ProgressWebService() を作成し、progressWebService.GetProgress() を呼び出すこともできました。次に、GetProgress() に渡されたコールバック ハンドラーが応答を処理し、UI を更新します。

ウェブサービス:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;
using System.Web.Caching;

namespace MyNamespace
{
    public class Progress
    {
        public string Message { get; set; }
        public bool Complete { get; set; }
    }

    [WebService(Namespace = "MyNamespace")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    [System.ComponentModel.ToolboxItem(false)]
    // To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line. 
    [System.Web.Script.Services.ScriptService]
    public class ProgressWebService : System.Web.Services.WebService
    {
        protected static Dictionary<string, Progress> ProgressMessages = new Dictionary<string, Progress>();

        [WebMethod]
        public Progress GetProgress(string progressId)
        {
            return ProgressMessages.ContainsKey(progressId) ? ProgressMessages[progressId] : new Progress();
        }

        [WebMethod]
        public void SetProgress(string progressId, string progress, bool complete)
        {
            if (ProgressMessages.ContainsKey(progressId))
            {
                ProgressMessages[progressId].Message = progress;
                ProgressMessages[progressId].Complete = complete;
            }
            else
                ProgressMessages.Add(progressId, new Progress() { Message = progress, Complete = complete });
        }

        [WebMethod]
        public void SetProgressComplete(string progressId, bool complete)
        {
            if (ProgressMessages.ContainsKey(progressId))
                ProgressMessages[progressId].Complete = complete;
            else
                ProgressMessages.Add(progressId, new Progress() { Complete = complete });
        }

        [WebMethod]
        public void AddProgress(string progressId, string progress)
        {
            if (ProgressMessages.ContainsKey(progressId))
                ProgressMessages[progressId].Message += progress;
            else
                ProgressMessages.Add(progressId, new Progress() { Message = progress });
        }
    }
}

クライアント側:

<%@ Page language="c#" CodeFile="About.aspx.cs" Inherits="MyNamespace.About" %>
<%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" TagPrefix="ajaxToolkit" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <script type="text/javascript">
        var ProgressServiceInterval; // global interval var, so it can be set and cleared across functions

        function btnBackup_Click(sender, e) {
            sender.disabled = true; // disable the backup button, so the request isn't duplicated
            // start getting the backup progress from the web service
            var progressService = new MyNamespace.ProgressWebService();
            progressService.SetProgressComplete('<%= strBackupProgressGuid %>', false, null, null, null);
            ProgressServiceInterval = setInterval('setBackupProgress()', 1000); // get progress once per second
        }

        function setBackupProgress() {
            var progressService = new MyNamespace.ProgressWebService();
            progressService.GetProgress('<%= strBackupProgressGuid %>', progressCallback, null, null);
        }

        function progressCallback(result) {
            var txtBackupOutput = $get('<%= txtBackupOutput.ClientID %>');
            try {
                // show the progress message
                txtBackupOutput.value = result.Message; 
                // stop checking if progress is complete
                if (result.Complete == true) clearInterval(ProgressServiceInterval);
                // scroll the textarea to the bottom
                txtBackupOutput.scrollTop = txtBackupOutput.scrollHeight - txtBackupOutput.clientHeight;
            } catch (ex) {

            }
        }
    </script>         
</head>
<body>
    <form id="frmMyForm" method="post" runat="server">
        <ajaxToolkit:ToolkitScriptManager runat="Server" EnablePartialRendering="true" EnablePageMethods="true" ID="ScriptManager1" >
            <Services>
                <asp:ServiceReference Path="ProgressWebService.asmx" />
            </Services>
        </ajaxToolkit:ToolkitScriptManager>

        <asp:UpdatePanel ID="updBackup" runat="server" RenderMode="Inline">
            <ContentTemplate>
                <asp:UpdateProgress ID="updBackupProgress" AssociatedUpdatePanelID="updBackup" runat="server" DynamicLayout="false">
                    <ProgressTemplate>
                        <div style="text-align:center;margin-bottom:-32px;"> 
                            <img src="loading.gif" alt="Loading..." />
                        </div>
                    </ProgressTemplate>
                </asp:UpdateProgress>

                <asp:Button ID="btnBackup" runat="server" CssClass="SubmitButton" Text="Back Up Data" UseSubmitBehavior="false" OnClientClick="btnBackup_Click(this, event);" />
                <br /><br />

                <asp:TextBox ID="txtBackupOutput" runat="server" ReadOnly="true" TextMode="MultiLine" Rows="10" Width="100%" Wrap="true" />  

            </ContentTemplate>
        </asp:UpdatePanel>
    </form>
</body>
</html>

そしてサーバー側:

namespace MyNamespace
{
    public partial class About
    {
        protected string strBackupProgressGuid
        {
            get
            {
                if (Session["strBackupProgressGuid"] == null)
                    Session["strBackupProgressGuid"] = Guid.NewGuid().ToString();
                return Session["strBackupProgressGuid"] as string;
            }
        }

        ProgressWebService _progressService;
        protected ProgressWebService progressService
        {
            get
            {
                return _progressService = _progressService ?? new ProgressWebService();
            }
        }

       void btnBackup_Click(object sender, EventArgs e)
        {
            progressService.SetProgress(strBackupProgressGuid, "Started\r\n", false);
            System.Threading.Thread.Sleep(10000);
            progressService.AddProgress(strBackupProgressGuid, "+10\r\n");
            System.Threading.Thread.Sleep(10000);
            progressService.AddProgress(strBackupProgressGuid, "+20\r\n");
            System.Threading.Thread.Sleep(10000);
            progressService.AddProgress(strBackupProgressGuid, "+30\r\n");

            progressService.SetProgressComplete(strBackupProgressGuid, true);
        }
    }   
}
于 2010-08-11T20:23:06.677 に答える
0

Web Developer Helperでさらに調査すると、設定した間隔 (5 秒) で Web サービス要求が行われているように見えますが、実行時間の長いタスク中の最初の要求は、結果を返すのにタスクの期間がかかりますが、後続のリクエストは引き続き何も返しません。タスクが完了すると、タスクの開始時に送信された最初の Web サービス要求がステータス データと共に返されます。

タスクが完了するまで、Web サービスへの最初の要求が返されない理由を突き止めようとしています。セッション変数は AsyncPostBack リクエストが終了するまで更新されない可能性があるため、ASP.NET キャッシュを試しましたが、これも機能しません。InstanceContextMode.PerSession モードと InstanceContextMode.Single モードの両方で、Web サービスでローカル変数を試しました。

MSDN Mag: July 2007: Cutting Edgeの例に従っていますが、ASP.NET キャッシュを使用しても AsyncPostBack には役に立たないようです。AsyncPostBack の代わりにコード ビハインドで WebMethod メソッドを直接呼び出してみるつもりですが、記事では動作するはずなので、実装が動作しない理由を突き止めたいと思います。

そう:

  1. ページの読み込み。
  2. setInterval('getUpdate()', 5000) が開始されます。
  3. getUpdate() は Web サービスを呼び出し、5 秒ごとに空のデータを返します。
  4. ユーザーがボタンをクリックして、UpdatePanel で AsyncPostBack を開始します。
  5. サーバー側の処理は、実行時間の長いタスクで開始されます。
  6. getUpdate() は Web サービスを呼び出します。リクエストは保留中です。
  7. 長時間実行タスクが続行されます。
  8. getUpdate() は、引き続き 5 秒ごとに Web サービスを呼び出します。各リクエストは空のデータを返します。
  9. 長時間実行タスクが完了します。
  10. AsyncPostBack が完了し、ブラウザーに応答を返します。
  11. 未処理の Web サービス要求には、応答が送信されます。
  12. getUpdate() は Web サービスの応答を返し、ページを更新して結果を表示します。
于 2010-05-24T23:56:12.317 に答える