这节是一节辅助内容。。将说清楚Lig日志系统中是怎样使用事件参数与委托参数的。
1) WCF服务接口中不能直接使用委托参数。
2) WCF采用回调接口来实现双向通信,我们可以将WCF回调接口消息封装为事件消息,在客户端接口订阅该事件消息,从而将Server消息发送给Client。
(WCF服务接口在Server端实现,WCF服务接口声明的回调接口在客户端实现)
8.1 WCF服务接口与回调接口
___________________________________________________________________________________________________________
[ServiceContract(CallbackContract = typeof(ILigAgentCallback))]
public interface ILigAgent
{
[OperationContract]
LigStatus RegisterClient(string name);
[OperationContract]
LigStatus UnregisterClient(int clientID);
[OperationContract]
LigStatus Subscribe(int clientID);
[OperationContract]
LigStatus Unsubscribe(int clientID);
[OperationContract]
void LigMessage(string message, LigLevel level);
[OperationContract]
void LigInfo(string message);
[OperationContract]
void LigDebug(string message);
[OperationContract]
void LigWarn(string message);
[OperationContract]
void LigError(string message);
[OperationContract(Name = "LigFatal")]
void LigFatal(string message);
[OperationContract(Name = "LigFatalex")]
void LigFatal(string message, Exception ex);
[OperationContract]
void SayHelloToServer(string message);
[OperationContract]
void Initialize(string ligPath);
}
public interface ILigAgentCallback
{
[OperationContract(IsOneWay = true)]
void OnNotifyMessage(string message);
[OperationContract(IsOneWay = true)]
void OnNotifyOnline(LigArgs args);
}
关键点:客户端ILigger接口出现委托参数
public interface ILigger
{
bool ConnectStatus { get; }
int ClientID { get; }
bool SubscribeOnlineEvent(EventHandler onlined);
bool UnsubscribeOnlineEvent(int clientID);
void LigMessage(string message, int level);
void LigInfo(string message);
void LigDebug(string message);
void LigWarn(string message);
void LigError(string message);
void LigFatal(string message);
}
我们在ILigger中看到了含委托参数的接口:
bool SubscribeOnlineEvent(EventHandleronlined);
为什么我们不直接在ILigAgent中加入这样的接口?下这种做法理论是是可行的,也不会导致程序编译错误。如果客户端调用下面的含委托参数的接口,会导致WCF客户端运行调用错误。。客户端调用会报一个通道状态为Fault的异常。。
那我们如来来用呢?
[OperationContract]
bool SubscribeOnlineEvent(EventHandleronlined);
8.1.1 WCF回调接口
___________________________________________________________________________________________________________
WCF的双向通信通过回调接口实现。每一个WCF服务接口都可以显式的声明一个回调接口
回调接口由客户端实现,这样我们可以在Server端通过
public sealed class OperationContext : IExtensibleObject<OperationContext>
中的成员获取客户端回调实例,然后用这该实例(T) 调用回调接口中的方法成员,从而将消息从服务端发送给指定客户端。。
public T GetCallbackChannel<T>();
___________________________________________________________________________________________________________
private event EventHandler onlineEvent;
private event EventHandler MessageRecv;
1) 回到LiggerBase类,我们Ligger使用的基类。的LiggerBase内部有两个事件参数:onlineEvent与MessageRecv。
2)在LiggerBase的内部我们定义了一个LigAgentCallback 实现了WCF的回调接口ILigAgentCallback,并在LiggerBase内部订阅了msgReceived事件与onlined事件
这样,WCF回调接口中的消息发送送到了LiggerBase内部的OnNotifyMessage与OnNotifyOnline。
【LiggerBase内部订阅LigAgentCallback的msgReceived事件与onlined事件】
private void SubscribedEvents()
{
this.callbackInstance.msgReceived += new MessageHandler(OnNotifyMessage);
this.callbackInstance.onlined += new OnlineHandler(OnNotifyOnline);
}
【WCF中LigAgentCallback 回调消息类】
#region InnerClasses
class LigAgentCallback : ILigAgentCallback
{
internal LigAgentCallback() { }
internal event MessageHandler msgReceived;
internal event OnlineHandler onlined;
public void OnNotifyMessage(string message)
{
if (this.msgReceived != null)
{
this.msgReceived(message);
}
}
public void OnNotifyOnline(LigArgs args)
{
if (this.onlined != null)
{
this.onlined(args);
}
}
internal void Release()
{
if (this.msgReceived != null)
{
Delegate[] items = this.msgReceived.GetInvocationList();
foreach (Delegate item in items)
{
this.msgReceived -= (MessageHandler)item;
}
}
if (this.onlined != null)
{
Delegate[] items = this.onlined.GetInvocationList();
foreach (Delegate item in items)
{
this.onlined -= (OnlineHandler)item;
}
}
}
}
#endregion
【ILigger接口订阅内部事件onlineEvent事件消息】
public bool SubscribeOnlineEvent(EventHandler onlined)
{
bool isSubscribed = false;
if (this.onlineEvent != null)
{
Delegate[] items = this.onlineEvent.GetInvocationList();
foreach (Delegate item in items)
{
if (onlined == (EventHandler)item)
{
isSubscribed = true;
break;
}
}
}
if (isSubscribed) return true;
this.onlineEvent += onlined;
return true;
}
【LiggerBase内部订阅的WCF回调接口消息 , 此处LiggerBase将WCF回调接口中的消息传递给了客户端订阅onlineEvent事件绑定的方法列表。。。】
private void OnNotifyOnline(LigArgs args)
{
LiggerEventArgs eventArgs = new LiggerEventArgs();
eventArgs.ClientID = args.ClientID;
eventArgs.ConnectStatus = args.ConnectStatus;
eventArgs.Message = args.Message;
this.clientID = args.ClientID;
this.connectStatus = args.ConnectStatus;
if (this.onlineEvent != null)
{
this.onlineEvent(this,eventArgs);
}
}
8.2 WCF服务接口事件封装解决之道
___________________________________________________________________________________________________________
1)默认条件下,WCF采用XML方式序列化,DataContractSerializer是WCF的默认序列化器,该类从XmlObjectSerializer类继承。Client与Server之间以XML Message方式进行信息交互。。
___________________________________________________________________________________________________________
2) 同时含委托与事件参数的类需要继承 MarshalByRefObject
___________________________________________________________________________________________________________
关于这个问题的最终答案,我也追寻了很久。。。。
实际上这不是本质的原因,本质原因是WCF默认序列化方式为 XML方式,而非事件与委托要求的SOAP。。不使用委托不是解决之道。这儿我们其实需要的是一种解决方法,而不是一段话。。。。
8.6 最后
___________________________________________________________________________________________________________
___________________________________
这儿。。。Ligger实际上只发生了对LiggBase的近接调用,注意此处我们没用Ligger继承LiggerBase,是因为Ligger不想暴露LiggerBase对WCF服务接口封装资源。。。这样让Client-Server间的耦合降低。。。。。。