天道酬勤

博观而约取,厚积而薄发!
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

基于WCF的事件发布/订阅系统

Posted on 2010-04-30 12:29  Happy Coding  阅读(1274)  评论(4编辑  收藏  举报

在WCF中,我们可以给service contract指定callbackContract来进行回调, 而回调接口由客户端实现。为了说明问题,我们设计了一个简单的scenario。

scenario description:

假设WCF service提供简单的AddTo(),即进行累积。客户端通过proxy消费这个service并将计算结果发布到所有注册了回调通道的其他客户端。

回调接口设计如下:


<!-- <br /><br />Code highlighting produced by ActiproCodeHighlighter (freeware)<br/>http://www.CodeHighlighter.com/<br /><br />-->[ServiceContract]
    
public interface ICaculatorCallBack
    {
        [OperationContract(IsOneWay 
= true)]
        
void Equals(int result);
    }

服务接口如下:

<!-- <br /><br />Codehighlighting produced by Actipro CodeHighlighter (freeware)<br/>http://www.CodeHighlighter.com/<br /><br />-->[ServiceContract(CallbackContract=typeof(ICaculatorCallBack),SessionMode=SessionMode.Required)]   
    
public interface ICaculatorService
    {
        [OperationContract(IsOneWay
=true)]
        
void AddTo(int n);

 

        [OperationContract(IsOneWay

=true)]
        
void Register();
    }

服务类型设计如下:

 


<!-- <br /><br />Code highlighting produced by ActiproCodeHighlighter (freeware)<br/>http://www.CodeHighlighter.com/<br /><br />-->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(InstanceContextMode

=InstanceContextMode.PerSession)]
    
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;           
        }

 

 

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");
            callback.Equals(e.result);
        }

 

 

public void BroadAddEvent(AddEventAgrs e, AddEventHandler temp)
        {
            
if (OnAddCompleted != null)
            {
                
foreach (AddEventHandler handler in temp.GetInvocationList())
                {
                    handler.BeginInvoke(
this, e, nullnull);
                }
            }
        }
       
        
public void AddTo(int n)
        {
            AddEventAgrs e 
= new AddEventAgrs();
            
this.result += n;
            e.result 
= result;
            BroadAddEvent(e, OnAddCompleted);
        }
    }
}

回调接口和服务契约非常简单,下面对serviceType作简单说明:

关于AddEventHandler:

当客户端调用AddTo这个服务的时候,服务器端开始计算,当计算完毕之后,然后开始广播并且调用各个客户端的回调实现。为了捕获计算完毕这个动作,因此我们必须定义一种类型的事件句柄并且申明相应类型的事件,因此本例中我们定义了一种AddEventHandle类型的事件OnAddCompleted

OnAddCompleted事件触发的时机:

那么OnAddCompleted事件是什么时候触发的呢?是在客户端消费AddTo服务的时候。 从AddToimplementation中我们可以看到:我们先将结算结果保存,并新建一个自定义事件,将计算结果保存至事件Args中,然后开始广播该事件(BroadAddEvent)。广播该事件的作用就是让所有注册了该事件(即消费了Register服务)的客户端开始调用其事件处理程序(CaculatorService_OnAddCompleted)。在该事件处理程序中,然后开始调用客户端的callbacb。

这就是整个利用callback机制进行广播的过程。

下面我们实现一个简单的客户端callback.如下:


<!-- <br /><br />Code highlighting produced by ActiproCodeHighlighter (freeware)<br/>http://www.CodeHighlighter.com/<br /><br />-->class CallBack:ICaculatorServiceCallback
    {
        
public void Equals(int n)
        {
           Console.WriteLine(
"this callback is implemented on client,the callback result is {0}", n.ToString());
        }
    }

客户端1主程序如下:


<!-- <br /><br />Code highlighting produced by ActiproCodeHighlighter (freeware)<br/>http://www.CodeHighlighter.com/<br /><br />-->using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

 

 

namespace WCFClient
{
    
class Program
    {
        
static void showMenu()
        {
            Console.WriteLine(
"Operation MENU\n--------------------");
            Console.Write(
"A:(Add) \nE:(exit):");
        }

 

 

static void Main(string[] args)
        {
          
            
try
            {
                showMenu();

 

                System.ServiceModel.InstanceContext callbackInstance 

= new System.ServiceModel.InstanceContext(new CallBack());
                CaculatorServiceClient proxy 
= new CaculatorServiceClient(callbackInstance);
                proxy.Open();
                proxy.Register();

 

 

string answer = Console.ReadLine();
                
while (answer.ToUpper() != "E")
                {
                    Console.Write(
"Please input the number to add:");
                    
int n = Convert.ToInt16(Console.ReadLine());
                    proxy.AddTo(n);
                    System.Threading.Thread.Sleep(
2000);
                    showMenu();
                    answer 
= Console.ReadLine();
                }
                proxy.Close();
              
            }
            
catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                Console.ReadLine();
            }
            
        }
    }
}

客户端2回调及主程序如下:


<!-- <br /><br />Code highlighting produced by ActiproCodeHighlighter (freeware)<br/>http://www.CodeHighlighter.com/<br /><br />--> class CallBack:ICaculatorServiceCallback
    {
        
public void Equals(int n)
        {
            Console.WriteLine(
"I have received a broadcasting news,the callback result is {0}", n.ToString());
        }
    }
 
class Program
    {
        
static void Main(string[] args)
        {
            
try
            {
                System.ServiceModel.InstanceContext callbackInstance 
= new System.ServiceModel.InstanceContext(new CallBack());
                CaculatorServiceClient proxy 
= new CaculatorServiceClient(callbackInstance);
                proxy.Open();
                Console.WriteLine(
"I am another listener, and I am receiving all broadcasting news\n--------------------------------------------");               
                proxy.Register();
                Console.ReadLine();
                proxy.Close();
            }
            
catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                Console.ReadLine();
            }
            
        }
    }

运行screenshot如下:

 总结:

事件发布/订阅模型有着广泛应用,比如实时任务调度,多人在线游戏,即时聊天,软件版本的自动更新等等。只有你想不到,没有你做不到:)

欲下载本文源代码,请点击此处