using System; using System.Collections.ObjectModel; using System.ServiceModel; using System.ServiceModel.Channels; using System.ServiceModel.Description; using System.ServiceModel.Dispatcher; namespace Utilities.Wcf.Interceptors { /// /// Provides numerous service interceptors/hooks for a WCF service, endpoint, or method. /// [AttributeUsage( AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true )] public class ServiceInterceptorAttribute : Attribute, IServiceBehavior, IEndpointBehavior, IOperationBehavior, IParameterInspector { private readonly Type _iServiceInterceptor; public Type ServiceType; #region Constructors public ServiceInterceptorAttribute() { } /// /// Initializes with an IServiceInterceptor. /// /// A type that implements IServiceInterceptor public ServiceInterceptorAttribute( Type iServiceInterceptor ) { // Enforce the type to implement IServiceInterceptor if ( !typeof( IServiceInterceptor ).IsAssignableFrom( iServiceInterceptor ) ) throw new ArgumentException( string.Format( "Wrong type: {0} must implement IServiceInterceptor", iServiceInterceptor ) ); _iServiceInterceptor = iServiceInterceptor; } #endregion Constructors #region Implementation of IServiceBehavior /// /// Provides the ability to inspect the service host and the service description to confirm that the service can run successfully. /// /// The service description.The service host that is currently being constructed. public virtual void Validate( ServiceDescription serviceDescription, ServiceHostBase serviceHostBase ) { } /// /// Provides the ability to pass custom data to binding elements to support the contract implementation. /// /// The service description of the service.The host of the service.The service endpoints.Custom objects to which binding elements have access. public virtual void AddBindingParameters( ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection endpoints, BindingParameterCollection bindingParameters ) { } /// /// Provides the ability to change run-time property values or insert custom extension objects such as error handlers, message or parameter interceptors, security extensions, and other custom extension objects. /// /// The service description.The host that is currently being built. public virtual void ApplyDispatchBehavior( ServiceDescription serviceDescription, ServiceHostBase serviceHostBase ) { ServiceType = serviceDescription.ServiceType; foreach ( var endpoint in serviceDescription.Endpoints ) { endpoint.Behaviors.Add( this ); foreach ( var operation in endpoint.Contract.Operations ) { if ( !operation.Behaviors.Contains( GetType() ) ) { operation.Behaviors.Add( this ); } } } } #endregion Implementation of IServiceBehavior #region Implementation of IEndpointBehavior /// /// Implement to confirm that the endpoint meets some intended criteria. /// /// The endpoint to validate. public virtual void Validate( ServiceEndpoint endpoint ) { } /// /// Implement to pass data at runtime to bindings to support custom behavior. /// /// The endpoint to modify.The objects that binding elements require to support the behavior. public virtual void AddBindingParameters( ServiceEndpoint endpoint, BindingParameterCollection bindingParameters ) { } /// /// Implements a modification or extension of the service across an endpoint. /// /// The endpoint that exposes the contract.The endpoint dispatcher to be modified or extended. public virtual void ApplyDispatchBehavior( ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher ) { } /// /// Implements a modification or extension of the client across an endpoint. /// /// The endpoint that is to be customized.The client runtime to be customized. public virtual void ApplyClientBehavior( ServiceEndpoint endpoint, ClientRuntime clientRuntime ) { } #endregion Implementation of IEndpointBehavior #region Implementation of IOperationBehavior /// /// Implement to confirm that the operation meets some intended criteria. /// /// The operation being examined. Use for examination only. If the operation description is modified, the results are undefined. public virtual void Validate( OperationDescription operationDescription ) { } /// /// Implements a modification or extension of the service across an operation. /// /// The operation being examined. Use for examination only. If the operation description is modified, the results are undefined.The run-time object that exposes customization properties for the operation described by . public virtual void ApplyDispatchBehavior( OperationDescription operationDescription, DispatchOperation dispatchOperation ) { dispatchOperation.ParameterInspectors.Add( this ); } /// /// Implements a modification or extension of the client across an operation. /// /// The operation being examined. Use for examination only. If the operation description is modified, the results are undefined.The run-time object that exposes customization properties for the operation described by . public virtual void ApplyClientBehavior( OperationDescription operationDescription, ClientOperation clientOperation ) { } /// /// Implement to pass data at runtime to bindings to support custom behavior. /// /// The operation being examined. Use for examination only. If the operation description is modified, the results are undefined.The collection of objects that binding elements require to support the behavior. public virtual void AddBindingParameters( OperationDescription operationDescription, BindingParameterCollection bindingParameters ) { } #endregion Implementation of IOperationBehavior #region Implementation of IParameterInspector /// /// Called before client calls are sent and after service responses are returned. /// /// The name of the operation. /// The objects passed to the method by the client. /// The correlation state that is returned as the correlationState parameter in AfterCall. Return null if you do not intend to use correlation state. public virtual object BeforeCall( string operationName, object[] inputs ) { object obj = null; var interceptor = GetServiceInterceptor(); if ( interceptor != null ) { obj = interceptor.BeforeCall( operationName, inputs ); } return obj; } /// /// Called after client calls are returned and before service responses are sent. /// /// The name of the invoked operation. /// Any output objects. /// The return value of the operation. /// Any correlation state returned from the BeforeCall method, or null. public virtual void AfterCall( string operationName, object[] outputs, object returnValue, object correlationState ) { var interceptor = GetServiceInterceptor(); if ( interceptor != null ) { interceptor.AfterCall( operationName, outputs, returnValue, correlationState ); } } #endregion Implementation of IParameterInspector #region Public Methods /// /// Attempts to get an IServiceInterceptor. /// /// /// This method attempts to first use a passed in IServiceInterceptor. /// If one was not passed in, then it attempts to use the current service instance /// if it implements IServiceInterceptor otherwise it returns null. /// /// An IServiceInterceptor public IServiceInterceptor GetServiceInterceptor() { if ( _iServiceInterceptor != null ) { var instance = Activator.CreateInstance( _iServiceInterceptor ); var serviceInterceptor = instance as IServiceInterceptor; if ( serviceInterceptor != null ) { return serviceInterceptor; } } var serviceInstance = OperationContext.Current.InstanceContext.GetServiceInstance(); if ( serviceInstance != null ) { var interceptor = serviceInstance as IServiceInterceptor; if ( interceptor != null ) { return interceptor; } } return null; } #endregion Public Methods } /// /// When used in conjunction with the ServiceInterceptorAttribute, this interfaces methods /// will be called at the appropriate time. /// public interface IServiceInterceptor { /// /// Called before client calls are sent and after service responses are returned. /// /// The name of the operation. /// The objects passed to the method by the client. /// The correlation state that is returned as the correlationState parameter in AfterCall. Return null if you do not intend to use correlation state. object BeforeCall( string operationName, object[] inputs ); /// /// Called after client calls are returned and before service responses are sent. /// /// The name of the invoked operation. /// Any output objects. /// The return value of the operation. /// Any correlation state returned from the BeforeCall method, or null. void AfterCall( string operationName, object[] outputs, object returnValue, object correlationState ); } }