自定义 Behavior
自定义Behavior 允许在WCF 构建运行时环境和消息处理管道的关键点上插入代码。
实现自定义行为的步骤:
1. 创建一个实现了 Inspector、Selector、Formatter、或Invoker 接口的类;
2. 创建一个实现了下列行为接口之一的类:IServiceBehavior、IEndpointBehavior、IOperationBehavior或IContractBehavior;
3. 配置客户端或服务来使用行为,可由 代码、配置文件、属性来完成;
服务端行为实现消息检验器
该Demo实现一个日志行为,输出端点发送和接收的每条消息,代码实现了从端点行为调用的一个消息检验器,以及在一个自定义扩管服务中手工的向服务描述添加端点行为。
public class MyEndpointBehavior : IEndpointBehavior { #region IEndpointBehavior Members public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { } public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { } public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new MyMessageInspector()); } public void Validate(ServiceEndpoint endpoint) { } #endregion } public class MyMessageInspector : IDispatchMessageInspector { #region IDispatchMessageInspector Members public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext) { Console.WriteLine(request.ToString()); return request; } public void BeforeSendReply(ref Message reply, object correlationState) { } #endregion }
执行结果:
结果分析:
服务端输出每次接收到的Message数据;
通过属性暴露服务操作行为的参数检验器
Demo功能说明: 使用正则表达式对参数进行校验,当参数无效时返回错误消息; 从操作行为调用参数检验器,实现属性的操作行为,在服务定义中通过属性引用将操作行为添加到服务描述中;
[AttributeUsage(AttributeTargets.Method)] public class MyOperationBehavior : Attribute, IOperationBehavior { public string Pattern; public string Message; #region IOperationBehavior Members public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters) { } public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation) { } public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation) { dispatchOperation.ParameterInspectors.Add(new MyParameterInspector(Pattern, Message)); } public void Validate(OperationDescription operationDescription) { } #endregion } public class MyParameterInspector : IParameterInspector { string pattern; string message; public MyParameterInspector(string pattern, string message) { this.pattern = pattern; this.message = message; } #region IParameterInspector Members public void AfterCall(string operationName, object[] outputs, object returnValue, object correlationState) { } public object BeforeCall(string operationName, object[] inputs) { foreach (object input in inputs) { if (input != null && input.GetType().Equals(typeof(string))) { Regex regex = new Regex(pattern); if (regex.IsMatch(input.ToString())) { throw new FaultException(string.Format("Parameter out of range : {0} , {1}", input.ToString(), message)); } } } return null; } #endregion } [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession, ConcurrencyMode = ConcurrencyMode.Multiple)] public class StockService : IStockService { public StockService() { Console.WriteLine("{0}:Created new instance of StockService on thread", DateTime.Now); } public class Worker { public string ticker; public Update workProcess; } public static Hashtable workers = new Hashtable(); public static object locker = new object(); private int callsCounter = 0; #region IStockService Members [MyOperationBehavior(Pattern="[^a-zA-Z]",Message="Only alpha characters allowed")] public double GetPrice(string ticker) { Console.WriteLine("{0}: GetPrice called on thread {1}", DateTime.Now, Thread.CurrentThread.ManagedThreadId); Thread.Sleep(5000); return 94.85; } public void RegisterForUpdate(string ticker) { Worker worker = null; if (!workers.ContainsKey(ticker)) { worker = new Worker(); worker.ticker = ticker; worker.workProcess = new Update(); worker.workProcess.ticker = ticker; workers.Add(ticker, worker); } worker = (Worker)workers[ticker]; IStockServiceCallback c = OperationContext.Current.GetCallbackChannel(); lock (worker.workProcess.callbacks) { worker.workProcess.callbacks.Add(c); } Thread t = new Thread(new ThreadStart(worker.workProcess.SendUpdateToClient)); t.IsBackground = true; t.Start(); } public StockPrice GetStockPrice(string ticker) { StockPrice p = new StockPrice(); Console.WriteLine("{0}: GetPrice called on thread {1}", DateTime.Now, Thread.CurrentThread.ManagedThreadId); p.Price = 94.85; lock (locker) { p.CallsCounter = ++callsCounter; } Thread.Sleep(1000); return p; } #endregion } public class Update { public string ticker; public List callbacks = new List (); public void SendUpdateToClient() { Random w = new Random(); Random p = new Random(); for (int i = 0; i < 10; i++) { Thread.Sleep(w.Next(5000)); lock (callbacks) { foreach (IStockServiceCallback callback in callbacks) { try { callback.PriceUpdate("msft", 100.00 + p.NextDouble()); } catch (Exception ex) { Console.WriteLine("Error sending cache to client:{0}", ex.Message); } } } } } }
执行结果
调试状态下直接报出错误信息
通过配置文件暴露服务行为
实现IServiceBehavior,代码:
public class MyServiceBehavior : IServiceBehavior { string evaluationKey; string evaluationType; public MyServiceBehavior(string evaluationKey, string evaluationType) { this.evaluationKey = evaluationKey; this.evaluationType = evaluationType; } #region IServiceBehavior Members public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collectionendpoints, BindingParameterCollection bindingParameters) { } public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { } public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { if (evaluationType.Equals("Enterprise", StringComparison.OrdinalIgnoreCase) & !evaluationKey.Equals("SuperSecretEvaluationKey", StringComparison.OrdinalIgnoreCase)) { throw new Exception(string.Format("Invalid evaluation key. Type:{0}", evaluationType)); } } #endregion }
配置: