响应式 WCF 服务(使用 ICallbackEventHandler 接口)






4.60/5 (5投票s)
具有ICallbackEventHandler接口的响应式WCF服务。
引言
在本文中,我将讨论一个WCF(双工)回调服务的开发,该服务会回复给消费者(即服务的客户端),并讨论ICallbackEventHandler
接口的实现,该接口在屏幕上为用户提供响应。
这里我设计了一个WCF服务,它为客户端下单,并回复客户端订单是否成功。ICallbackEventHandler
是在页面上实现的接口,用于向客户端显示响应。在进一步讨论之前,这里有一个屏幕显示输出
下单
WCF服务下单
订单是否成功
为了实现这一点,程序分为两部分
- 回调WCF服务的设计。
- 带有
ICallbackEventHandler
接口的网页设计,用于在屏幕上提供响应。
回调WCF服务配置文件设计
您需要像下面这样配置WCF服务,以使服务可靠(双工),即在任务完成后将响应发送回客户端。
<configuration>
<system.servicemodel>
<bindings>
<wsdualhttpbinding>
<binding bypassproxyonlocal="true" name="sampleBinding" usedefaultwebproxy="true">
</binding></wsdualhttpbinding>
</bindings>
<services>
<service behaviorconfiguration="returnFaults" name="Service.DemoService">
<endpoint binding="wsDualHttpBinding"
bindingconfiguration="sampleBinding" contract="Service.IDemoService">
</endpoint></service>
</services>
<behaviors>
<servicebehaviors>
<behavior name="returnFaults">
<servicedebug includeexceptiondetailinfaults="true">
<servicemetadata httpgetenabled="true">
</servicemetadata></servicedebug></behavior>
</servicebehaviors>
</behaviors>
</system.servicemodel>
<system.web>
<compilation debug="true">
</compilation></system.web>
</configuration>
配置文件中需要注意的点是使用的协议wsDualHttpBinding
,它允许创建可靠会话,这意味着一旦任务完成,它允许将响应发送回调用服务的客户端。 WSDualHttpBinding - 一个安全且可互操作的绑定,设计用于双工服务契约,允许服务和客户端发送和接收消息。
WCF服务文件
创建或修改后,您可以按如下方式编写服务文件
using System;
using System.ServiceModel;
using System.Collections.Generic;
using System.Threading;
using System.Runtime.Serialization;
namespace Service
{
IDemoService
- 由WCF服务实现的接口,其方法由使用WCF服务的应用程序调用。
[ServiceContract(CallbackContract = typeof(IClientCallBack))]
public interface IDemoService
{
[OperationContract(IsOneWay = true)]
void PlaceOrder(OrderItem item);
}
ServiceContractAttribute.CallbackContract
- 此属性允许在契约是双工契约时设置回调契约,即服务调用以通知客户端的回调接口。因此,这允许客户端应用程序侦听服务应用程序可以发送到客户端应用程序的入站操作调用,而这与客户端活动无关。具有单向操作的回调契约表示来自服务且客户端可以处理的调用。
IClientCallBack
- 由使用WCF服务的应用程序(即客户端)实现的接口。此接口的方法由WCF服务方法调用,如下面讨论的。
public interface IClientCallBack
{
[OperationContract(IsOneWay = true)]
void ISOrerPlaceSuccessfully(bool issuccess, float total);
}
OrderItem
- 是WCF服务的数据契约类。
[DataContract]
public class OrderItem
{
float price;
string name;
int qty;
[DataMember]
public float Price
{
get { return price; }
set { price = value;}
}
[DataMember]
public string Name
{
get { return name; }
set { name = value; }
}
[DataMember]
public int Quantity
{
get { return qty; }
set { qty = value; }
}
}
DemoService
- 实现服务契约接口。
public class DemoService : IDemoService
{
public void PlaceOrder(OrderItem item)
{
IClientCallBack callback = OperationContext.Current.GetCallbackChannel<IClientCallBack>();
bool success = true;
//process order
float total = item.Price * item.Quantity;
callback.ISOrerPlaceSuccessfully(success, total);
}
}
PlaceOrder
- 调用回调契约方法ISOrerPlaceSuccessfully
的方法。
OperationContext.Current
- 获取当前线程的执行上下文。代码使用Current
属性和GetCallbackChannel<t>
方法从服务方法创建回调用者的通道,即客户端。单向方法允许服务和客户端双向独立通信。
带有ICallbackEventHandler接口的网页设计,用于在屏幕上提供响应
<%@ Page Language="C#" AutoEventWireup="true"
CodeBehind="Default.aspx.cs" Inherits="WebClient.Default" EnableSessionState="True"%>
<!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">
<title></title>
</head>
<body id="formBody" runat="server" >
<form id="form1" runat="server">
<div style="text-align:center">
<h1>Place Order</h1>
</div>
<div>
<table>
<tr>
<td><asp:Label ID="lblItemName" runat="server" Text="Label">Item Number :</asp:Label></td>
<td><asp:Label ID="lblItemValue" runat="server" Text="Label">Test</asp:Label></td>
</tr>
<tr>
<td><asp:Label ID="Label1" runat="server" Text="Label">Price :</asp:Label></td>
<td><asp:Label ID="Label2" runat="server" Text="Label">500</asp:Label></td>
</tr>
<tr>
<td><asp:Label ID="Label3" runat="server" Text="Label">Qunatity :</asp:Label></td>
<td><asp:TextBox ID="txtQunatity" runat="server" ></asp:TextBox></td>
</tr>
</table>
<asp:Button ID="Button1" runat="server" Text="Place Order" OnClick="Button1_Click" />
<asp:Label ID="lblMsg" runat="server"></asp:Label></div>
</form>
</body>
</html>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using WebClient.DemoService;
using System.ServiceModel;
using System.Threading;
namespace WebClient
{
客户端回调接口的实现
这里实现了服务中作为回调契约一部分声明的所有方法。该方法在服务端任务完成后由服务方法调用。
public class CallBack : WebClient.DemoService.IDemoServiceCallback
{
public string Message
{
get;set;
}
public void ISOrerPlaceSuccessfully(bool issuccess, float total)
{
Thread.Sleep(5000);
if (issuccess)
this.Message = "Order with total of : " + total + " placed successfully";
else
this.Message = "Order with total of : " + total + " failed to place";
}
}
ISOrerPlaceSuccessfully
- 是回调方法,由服务的placeorder
方法回调。响应到达后,Message
属性值将更新,用于在用户屏幕上显示订单状态。注意 - 代码中的Thread.Sleep
仅用于延迟/演示目的,即展示当服务中调用一个较长进程时,它是如何工作的。在项目中使用时请将其删除。
ICallbackEventHandler
接口 - 用于指示控件可以是服务器上回调事件的目标。ICallbackEventHandler
是XMLHTTP
的包装器。它允许在不进行回发的情况下调用服务器端方法,并且无需编写任何JavaScript/jQuery来发起AJAX调用。
public partial class Default : System.Web.UI.Page, ICallbackEventHandler
{
static CallBack callback;
DemoServiceClient client;
protected void Page_Load(object sender, EventArgs e)
{
}
protected void Button1_Click(object sender, EventArgs e)
{
setupClientSideCallback();
callback = new CallBack();
InstanceContext ic = new InstanceContext(callback);
client = new DemoServiceClient(ic);
OrderItem item = new OrderItem();
item.Name = "Test";
item.Price = 12;
item.Quantity = Convert.ToInt32(txtQunatity.Text);
lblMsg.Text = "Placing Order...";
client.PlaceOrder(item);
}
在上面的代码中,回调是Callback
类型的静态变量,因为它在“Place Order”按钮被点击时,回调的值保持不变。也就是说,它不会在后面的GetCallbackResult
方法(下文讨论)中再次创建以供后续使用。
OnCallback
是客户端定义的一个方法,用于在客户端脚本由服务器端代码注册后进行回调,而eventArgs
是客户端定义的一个变量,用于保存参数值。
protected void setupClientSideCallback()
{
string ScriptRef = this.ClientScript.GetCallbackEventReference(this, "'" + 0 + "'",
"OnCallback", "'" + lblMsg.ClientID + "'");
formBody.Attributes.Add("onload", ScriptRef);
string script = "<script language="javascript" type="text/javascript">" +
" function getResponse() " +
" { " + ScriptRef + " } " +
" function OnCallback(Result,Context) " +
" { " +
" var lbl=document.getElementById(Context); " +
" lbl.innerText=Result ; " +
" setTimeout(getResponse, 1000); " +
" } " +
"
</script> ";
this.ClientScript.RegisterClientScriptBlock(this.GetType(), "ClientCallback", script);
}
JavaScript代码 - 在点击“Place Order”按钮时注册。之后,为了检查WCF服务是否返回了响应,它使用setTimeout
函数定期发出调用。因此,它就像一个定时器,定期执行一组代码。ICallbackEventHandler
接口有两个方法需要在页面中实现。
RaiseCallbackEvent
- 当从客户端(JavaScript),即从浏览器到服务器的调用发生时,会调用此事件。这是处理回调处理程序的事件。这里eventArgs
是从客户端传递过来的参数。
GetCallbackResult
- 此方法将回调事件的结果返回到客户端,即从服务器端到浏览器。
string ICallbackEventHandler.GetCallbackResult()
{
if (callback!=null && callback.Message != null)
{
return callback.Message;
}
return "Placing Order..." ;
}
string eventArgument;
void ICallbackEventHandler.RaiseCallbackEvent(string eventArgument)
{
this.eventArgument = eventArgument;
}
}
}
摘要
WCF可靠(双工)服务对于告知服务消费者(即服务客户端)任务是否已完成很有用,它通过服务调用消费者(即客户端)来实现。这种东西在创建支付订单等模块时很有用,就像我在我的实现中所做的那样。