后续解决方案适用于任何未来可能会遇到的问题。
我试图发送请求的方式多种多样:当您添加WCF服务的服务引用到的ScriptManager
- 的AJAX脚本对象创建。因此,如果WCF服务类是带有GetProgress方法的ProgressService,我在JavaScript中创建了一个新的ProgressService对象,并称为progressService.GetProgress()。
- XmlHttpRequest请求。
- jQuery $ .getJson()请求。
- 一个Sys.Net.WebRequest请求。
- 一个Sys.Net.WebServiceProxy请求。
事实证明,即使客户端请求被发送,即没有被ASP.NET ScriptManager缓存,如果它是IIS中同一网站的一部分,WCF服务也不会响应。
因此,我转而使用传统的ASP.NET(SOAP)Web服务(.asmx),而不是创建完全独立的WCF项目和完全独立的IIS网站。
我能够保持.asmx服务在Visual Studio中的同一个项目的一部分,并在IIS中的网站。请求在回发期间发送,服务在回发期间响应。
在ScriptManager中将它作为ServiceReference添加后,我也能够使用基本相同的脚本对象,创建一个新的ProgressWebService(),然后调用progressWebService.GetProgress()。传递给GetProgress()的回调处理程序然后处理响应并更新UI。
Web服务:
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);
}
}
}
克里斯 - 你的代码中手动排队时回传被发起的请求,然后在当前回发完成发送请求。这不符合这里所述的目标,因为1)要点是在回发期间不断发送进度信息请求,而不是一旦回传完成,以及2)它针对客户端,这不是问题;问题原来是服务器端的WCF服务。另外,当为WCF或ASMX服务添加服务引用时,比使用手动回发来使用由.Net创建的代理对象更容易。 – nekno 2011-07-16 08:04:36