在WCF應用callback模式時,一些指導性的document給出的建議是將callback contract內的operation的Message Exchange Pattern設定為IsOneWay.
當然,當service調用callback而不需要從client instance獲得callback的return value的時候,MEP設定為IsOneWay不失為最好的選擇了。但是如果在某些應用情境下,我們需要獲得用戶端callback結果時候,就不能將MEP設定為IsOneWay=true了。
當然,當MEP設定為IsOneWay=false的時候,其他同步設定必須相應match,即ConcurrentMode只能為ReEntrant或者Multiple。
下面給出一個簡單的應用模式:線上即時投票。
其簡單系統模型如下:
WCF service提供一個簡單的投票服務,收集投票結果通過callback機製得到各個client的結果後然後返回給service,然後WCF service給出statistics.
針對這樣的投票模型,在設定service的時候需要注意的是:
1.callback contract中的operation一定要設定為IsOneWay=false。否則callback無法return至服務端
2.ConcurrentMode不能設定為Single,而只能為ReEntrant或者Multiple.
3. 當service callbacks client的時候,callback result的處理權在service,比如對結果進行統計處理等等。
下面給出主要的source code:
callback contract和service contract的定義
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
namespace WCFCallBack
{
[ServiceContract]
public interface ICaculatorCallBack
{
[OperationContract]
int Equals(int result);
}
[ServiceContract(CallbackContract=typeof(ICaculatorCallBack))]
public interface ICaculatorService
{
[OperationContract]
void AddTo(int n);
[OperationContract]
void Register();
}
}
servicecontract implementation以及訊息發布
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
namespace WCFCallBack
{
public class AddEventAgrs : EventArgs
{
public int result;
}
[ServiceBehavior(ConcurrencyMode=ConcurrencyMode.Reentrant)]
class CaculatorService:ICaculatorService
{
public delegate void AddEventHandler(object sender, AddEventAgrs e);
public static event AddEventHandler OnAddCompleted;
public ICaculatorCallBack callback;
public int result;
public CaculatorService()
{
result = 0;
Console.WriteLine("A Calculator Instance is constructed");
}
public void Register()
{
callback = System.ServiceModel.OperationContext.Current.GetCallbackChannel<ICaculatorCallBack>();
OnAddCompleted += new AddEventHandler(CaculatorService_OnAddCompleted);
}
void CaculatorService_OnAddCompleted(object sender, AddEventAgrs e)
{
Console.WriteLine("the OnAdd event has been triggered");
Console.WriteLine("the callback result from client is "+callback.Equals(e.result));
}
public void BroadAddEvent(AddEventAgrs e, AddEventHandler temp)
{
if (OnAddCompleted != null)
{
foreach (AddEventHandler handler in temp.GetInvocationList())
{
handler.BeginInvoke(this, e, null, null);
}
}
}
public void AddTo(int n)
{
AddEventAgrs e = new AddEventAgrs();
this.result += n;
e.result = result;
BroadAddEvent(e, OnAddCompleted);
}
}
}
client1 callback實現:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace WCFClient
{
class CallBack:ICaculatorServiceCallback
{
public int Equals(int n)
{
//Console.WriteLine("haha");
Console.WriteLine("this callback is implemented on client,the callback result is {0}", n.ToString());
return 2*n ;
}
}
}
client2 callback implementation
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace WCFClient
{
class CallBack:ICaculatorServiceCallback
{
public int Equals(int n)
{
Console.WriteLine("I have received a broadcasting news,the callback result is {0}", n.ToString());
System.Threading.Thread.Sleep(2000);
return n * n;
}
}
}
運行結果:
service screenshot
client1 screenshot
client 2 screenshot