WCF Extensibility Scott Reed Owner - Brain Hz Software scott@brainhzsoftware.com Instructor – DevelopMentor scottr@develop.com WCF Design Goal • To be the single best way of getting any two pieces of software to communicate under any circumstances (assuming at least one is .NET) Extensibility in a nutshell • WCF doesn’t support everything out of the box • When a limitation is reached WCF can still be used by adding or replacing a specific piece of the framework • (Almost) everything can be extended • Infinitely flexible = more difficult than it needs to be Client Service C B A A B C C B A A B C Service A B C Client Code Service Type parameters parameters Service Model Layer Client Runtime Dispatcher Runtime Channel Channel Channel Channel Encoder Channel Layer Encoder Transport Channel Transport Channel byte[] byte[] ServiceHost ChannelDispatcher EndpointDispatcher DispatchRuntime DispatchOperation ChannelStack IChannel (Protocol) IChannel (Protocol) IChannel (Transport) Behavior extensibility steps 1) Implement the functionality – MessageInspector, ErrorHandler, InstanceProvider, etc. 2) Hook up the functionality through behavior – ServiceBehavior, EndpointBehavior, OperationBehavior 3) Let WCF know about the behavior from #2 – Programmatically, configuratively, declaratively When Behaviors Run 1. Reflect Over T [MyBehavior] ServiceHost() or ChannelFactory() 2. Load Config .Description <myBehavior /> .Description.Behaviors.Add(new MyBehavior()); 3. Build Runtime .Open(); 4. Open Resources Behaviors Three Behaviors • All three behavior types have the same methods: public void AddBindingParameters(<description>, BindingParameterCollection); public void ApplyClientBehavior(<description>, <client plumbing>);* public void ApplyDispatchBehavior(<description>, <service plumbing>); public void Validate(<description>); * ApplyClientBehavior isn’t present in IServiceBahvior Parameter Inspection Useful for consistent client and service side validation Parameter Inspectors Client Operation Client Runtime Channel public interface IParameterInspector { object BeforeCall(string operationName, object[] inputs); void AfterCall(string operationName, object[] outputs, object returnValue, object correlationState); } Message Formatting Actually creates the message from parameters, and breaks apart the message into parameters and return value. Formatter Client Operation Client Runtime Channel public interface IClientMessageFormatter { Message SerializeRequest(MessageVersion messageVersion, object[] parameters); object DeserializeReply(Message message, object[] parameters); } Message Inspection The most useful, allows last minute manipulation of the message before being sent into the channel layer. Message Inspectors Client Operation Client Runtime Channel public interface IClientMessageInspector { object BeforeSendRequest(ref Message request, IClientChannel channel); void AfterReceiveReply(ref Message reply, object state); } Other client side extension points • Via (sends to intermediaries) • ChannelInitializers (manipulate channel stack) • But the service has far more extensibility… Demo • Client side message inspector • Adding a behavior extension element Error Handling Allows a central place to perform exception to fault message translation. Error Handlers ChannelDispatcher EndpointDispatcher DispatchRuntime DispatchOperation public interface IErrorHandler { bool HandleError(Exception error); void ProvideFault(Exception error, MessageVersion version, ref Message fault); } Address / Contract Filtering Allows the messages to be matched to an appropriate dispatch runtime. Address Filter ChannelDispatcher Contract Filter EndpointDispatcher DispatchRuntime DispatchOperation public abstract class MessageFilter { IMessageFilterTable<FilterData> CreateFilterTable<FilterData>(); bool Match(Message message); } Providing InstanceContext Allows special case instancing modes Instance Context Provider ChannelDispatcher EndpointDispatcher DispatchRuntime DispatchOperation public interface IInstanceContextProvider { InstanceContext GetExistingInstanceContext(Message message, IContextChannel channel); void InitializeInstanceContext(InstanceContext instanceContext, Message message, IContextChannel channel); bool IsIdle(InstanceContext instanceContext); void NotifyIdle(InstanceContextIdleCallback callback, InstanceContext instanceContext);} Providing Instance Allows service object pooling Instance Provider ChannelDispatcher EndpointDispatcher DispatchRuntime DispatchOperation public interface IInstanceProvider { object GetInstance(InstanceContext instanceContext,Message message); void ReleaseInstance(InstanceContext instanceContext,object instance); } Message Inspection (again) The most useful, allows manipulation of the message right after being received from the channel layer. Message Inspectors ChannelDispatcher EndpointDispatcher DispatchRuntime DispatchOperation public interface IDispatchMessageInspector { object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext); void BeforeSendReply(ref Message reply, object correlationState); } Operation Selection Allows operation selection by something other than Action Operation Selector ChannelDispatcher EndpointDispatcher DispatchRuntime DispatchOperation public interface IDispatchOperationSelector { string SelectOperation(ref Message message); } public SynchronizedKeyedCollection<string, DispatchOperation> Operations { get { } } Message Formatting (again) Actually creates the message from parameters, and breaks apart the message into parameters and return value. Formatter ChannelDispatcher EndpointDispatcher DispatchRuntime DispatchOperation public interface IDispatchMessageFormatter { void DeserializeRequest(Message message, object[] parameters); Message SerializeReply(MessageVersion messageVersion, object[] parameters, object result); } Operation Invocation Actually takes control of calling the service object. Invoker ChannelDispatcher EndpointDispatcher DispatchRuntime DispatchOperation public interface IOperationInvoker { object[] AllocateInputs(); object Invoke(object instance, object[] inputs, out object[] outs); IAsyncResult InvokeBegin(object instance, object[] inputs, ...); object InvokeEnd(object instance, out object[] outputs, ...); } Parameter Inspection (again) Useful for consistent client and service side validation. Parameter Inspectors ChannelDispatcher EndpointDispatcher DispatchRuntime DispatchOperation public interface IParameterInspector { object BeforeCall(string operationName, object[] inputs); void AfterCall(string operationName, object[] outputs, object returnValue, object correlationState); } Other service extensibility points • • • • • • • • • • • Extensions ChannelInitializers ServiceThrottle ExternalAuthorizationPolicies RoleProvider ServiceAuthorizationManager InputSessionShutdownHandlers InstanceContextInitializers FaultContractInfos CallContextInitializers Etc... Demo • Service side error handler Channel Layer extensibility • Easy ways: – Binding configuration – Programmatic binding manipulation – Custom bindings • Hard way – Writing a protocol channel • Really hard way – Writing a transport channel Writing a Channel (Overview) • To write a channel, you must – Write the channel itself • Support the proper “shapes” – Input, Output, Duplex, Request, Reply, RequestContext • Possibly provide asynchronous versions of all method – Write a ChannelFactory – Write a ChannelListener – Write a BindingElement Extensibility Summary • If possible stick with Service Model layer – There are tons of extension points – It’s not that hard to do • If you have to write a channel – Plan on spending a couple of weeks