WCF中使用观察者模式实现事件通知

    看了一些WCF的例子,实现事件通知使用的是多播委托的特性,有点复杂,操作起来也不是很直观,看到一堆委托和事件我一般头就晕了。下面介绍一种使用观察者模式实现事件通知的简单方法。没别的,就是简单,简单最美。
    工程代码如下:https://files.cnblogs.com/dyj057/WcfEvent.rar
    1.定义接口
[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(IWriteLogCallback))]
    
public interface ILogService
    {
        [OperationContract(IsInitiating 
= true, IsTerminating = false)]
         
void Write(string logMsg);

        [OperationContract(IsInitiating 
= true, IsTerminating = false)]
        
void RegisterListener();

        [OperationContract(IsInitiating 
= false, IsTerminating = false)]
        
void UnregisterListener();
    }

    [ServiceContract]
    
public interface IWriteLogCallback
    {
        [OperationContract(IsOneWay 
= true)]
        
void OnWriteLog(string logMsg);
    }

为了简单举了一个写日志的例子, Write(string logMsg)就是写入日志的方法,参数logMsg是需要写入的日志信息。当客户单没有调用RegisterListener()订阅事件的时候,是不会收到写日志的事件通知的,相应的要获得写日志的事件通知,就需要调用RegisterListener()方法。如果要取消订阅就调用UnregisterListener()方法。写日志的功能和事件的订阅功能是分开的。

    2.服务实现
[ServiceBehavior(
            IncludeExceptionDetailInFaults 
= true,
            InstanceContextMode 
= InstanceContextMode.Single,
            ConcurrencyMode 
= ConcurrencyMode.Multiple)]
    
class LogService:ILogService
    {
        
public LogService()
        {
            Trace.WriteLine(
"Create LogService Instance.");
        }

        Dictionary
<string, OperationContext> listeners = new Dictionary<string, OperationContext>();

        
private void BroadCast(string logMsg)
        {
            List
<string> errorClints = new List<string>();

            
foreach (KeyValuePair<string, OperationContext> listener in listeners)
            {
                
try
                {
                    listener.Value.GetCallbackChannel
<IWriteLogCallback>().OnWriteLog(logMsg);
                }
                
catch (System.Exception e)
                {
                    errorClints.Add(listener.Key);
                    Trace.WriteLine(
"BROAD EVENT ERROR:" + e.Message);
                }
            }

            
foreach (string id in errorClints)
            {
                listeners.Remove(id);
            }
        }

        
#region ILogService 成员

        
public void Write(string logMsg)
        {
            Trace.WriteLine(
"Write LOG:"+logMsg);
            BroadCast(logMsg);
        }

        
public void RegisterListener()
        {
            listeners.Add(OperationContext.Current.SessionId, OperationContext.Current);

            Trace.WriteLine(
"SessionID:" + OperationContext.Current.SessionId);
            Trace.WriteLine(
"Register listener. Client Count:" + listeners.Count.ToString());
        }

        
public void UnregisterListener()
        {
            listeners.Remove(OperationContext.Current.SessionId);
            Trace.WriteLine(
"SessionID:" + OperationContext.Current.SessionId);
            Trace.WriteLine(
"Unregister listener. Client Count:" + listeners.Count.ToString());
        }

        
#endregion
    }
    Dictionary<string, OperationContext> listeners包含了所有的事件订阅者。发布事件的时候,如果调用订阅者的回调函数失败,就把该订阅者从listeners移除。代码很简单,就不多说了。
    3.客户端访问
    定义回调的客户端:
class LogClient:IWriteLogCallback
    {
        
#region IWriteLogCallback 成员

        
public void OnWriteLog(string logMsg)
        {
            Trace.WriteLine(
"RECV LOG EVENT:" + logMsg);
        }

        
#endregion
    }

    然后在程序中使用它:
class Program
    {
        
static void Main(string[] args)
        {
            Trace.Listeners.Add(
new ConsoleTraceListener());
            LogClient client 
= new LogClient();
            ILogService service 
= DuplexChannelFactory<ILogService>.CreateChannel(client,
                
new WSDualHttpBinding(), new EndpointAddress("http://localhost:8888/log"));
            
            
//订阅消息
            service.RegisterListener();
            service.Write(
"Client start");
            
            Console.WriteLine(
"Press enter key to exit.");
            Console.ReadLine();

            service.UnregisterListener();
        }

需要注意的问题:
A. 因为客户也要监听端口,所以确保防火墙没有对它进行阻止。
B. 这里使用的是单实例的服务,所以需要进行多进程访问的保护,才能实际使用。
posted @ 2008-04-25 17:28  南桥一梦  阅读(4049)  评论(5编辑  收藏  举报