将来これを見つける可能性のある人のための解決策のフォローアップ。
さまざまな方法でリクエストを送信してみました:
- 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);
}
}
}