自定义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
    }

执行结果:

BasemapGallery 自定义 自定义behavior_自定义

结果分析:

服务端输出每次接收到的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 }

配置: