自定义Behavior 允许在WCF 构建运行时环境和消息处理管道的关键点上插入代码。
自定义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, Collection endpoints, 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 }
配置: