470 likes | 624 Views
CON 413. WCF Architecture and Extensibility P oints. Justin Smith Technical Evangelist http://blogs.msdn.com/justinjsmith. Agenda. Bindings Channel Managers Channels Messages XmlWriters and XmlReaders Message Encoders Dispatcher and Behaviors. WCF Architecture: Layers of Layers.
E N D
CON 413 WCF Architecture and Extensibility Points Justin Smith Technical Evangelist http://blogs.msdn.com/justinjsmith
Agenda Bindings Channel Managers Channels Messages XmlWriters and XmlReaders Message Encoders Dispatcher and Behaviors
WCF Architecture: Layers of Layers Sender Receiver Sender Application Receiver Application ServiceModel Layer Proxy Dispatcher Channels Channels Channel Layer
Agenda Bindings Channel Managers Channels Messages XmlWriters and XmlReaders Message Encoders Dispatcher and Behaviors
What is a Binding? • Abstraction of messaging functionality • Transport, security, transaction options, protocols • Creates a collection of BindingElement objects • Arrangement in collection is important NetTcpBinding b = new NetTcpBinding(); foreach (BindingElement el in b.CreateBindingElements()) { Console.WriteLine(el.GetType().Name); } // outputs TransactionFlowBindingElement // protocol BinaryMessageEncodingBindingElement // encoding WindowsStreamSecurityBindingElement // security TcpTransportBindingElement // transport
What is a BindingElement? • Create Channel Listeners & Channel Factories • listeners on receiver / factories on sender • collectively known as Channel Managers • Collection creates a stack (listeners/factories) • via BindingContext and CustomBinding types • BindingContext keeps a mutable list of BindingElement objects in Collection • CustomBinding is isomorphic
Walkthrough : Channel Factory NetTcpBinding BuildChannelFactory<T> BindingContext Channel Factory Stack CustomBinding TransactionFlowBindingElement TransactionFlowChannelFactory<T> BinaryMessageEncodingBindingElement BuildInnerChannelFactory<T> WindowsStreamSecurityBindingElement TcpTransportBindingElement TcpChannelFactory<T> Encoding and StreamSecurity absorbed by TcpChannelFactory via BindingContext
Walkthrough : Channel Factory public abstract class Binding : IDefaultCommunicationTimeouts { public virtual IChannelFactory<T> BuildChannelFactory<T> ( BindingParameterCollection parameters) { BindingContext context1 = new BindingContext(new CustomBinding(this), parameters); IChannelFactory<T> factory1 = context1.BuildInnerChannelFactory<T>(); return factory1; } // creates BindingContext, then calls BuildInnerChannelFactory } public class BindingContext { public IChannelFactory<T> BuildInnerChannelFactory<T>() { return this.RemoveNextElement().BuildChannelFactory<T>(this); } // removes B.E. from list, then calls BuildChannelFactory on it } public class MyBindingElement : BindingElement { public virtual IChannelFactory<T> BuildChannelFactory<T>(BindingContext c) { return new MyChannelFactory<T>(c); // ctor calls context’s } // returns the Channel Factory // BuildInnerChannelFactory }
Agenda Bindings Channel Managers Channels Messages XmlWriters and XmlReaders Message Encoders Dispatcher and Behaviors
What is a Channel Factory? • Create sending Channels • Stack of Channel Factories creates Channel stack • Channel Factory stack is opaque • GetProperty<T> allows some query capabilities • Each Channel Factory in stack: • Refers to the next Channel Factory • Delegates GetProperty<T> calls when unknown • EndpointAddress needed in channel stack • Uri for Via can be used as well
What is a Channel Listener? • Listens for incoming messages & Creates receiving channel stacks • Bottom-most Channel Listener listens on transport • Embrace “Accept” semantics similar to sockets • “Accept” returns a • Channel Listener stacks are opaque • Symmetry with Channel Factory design
Walkthrough : Channel Stack From a Channel Factory: Channel Factory Stack CreateChannel Channel Stack TransactionChannelFactory<T> TransactionChannel<T> CreateChannel TcpChannelFactory<T> TcpChannel<T> From a Channel Listener: Channel Listener Stack AcceptChannel Channel Stack TransactionChannelListener TransactionChannel<T> TcpChannelListener TcpChannel<T> AcceptChannel message passing to channel stack not precise
Agenda Bindings Channel Managers Channels Messages XmlWriters and XmlReaders Message Encoders Dispatcher and Behaviors
What is a Channel? • Sends and/or receives Message objects • For specific transport or protocol functionality • Commonly stacked w / other Channel objects • Have shape described by interfaces • MEP (datagram, request/reply, duplex) • Composition of channel stack dictates messaging functionality
CreateSequence CreateSequenceResponse (ID) Sequence (ID, Msg #1) X Sequence (ID, Msg #2) Sequence (ID, Msg #3, LastMessage) SequenceAck (ID, Msg #1, Msg #3) Sequence (ID, Msg #2, AckRequested) SequenceAck (ID, Msg #1 - Msg #3) TerminateSequence (ID) Consider WS-RM Sender Receiver
Walkthrough: WS-RM Sending Application Client Channel Stack ReliableOutputSessionChannel Sequence (#2, LastMessage) CreateSequence / CreateSequenceResponse Sequence (#1) Ack (Msg #1-2) TerminateSequence HttpRequestChannel<T> Receiving Application
MEPs and The Channel Layer • Channel Managers and Channels have shape • Shape represents the supported MEP(s) • Datagram, Request/Reply, Duplex • WCF represents shape via interfaces • Datagram: IInputChannel, IOutputChannel • Request / Reply: IRequestChannel, IReplyChannel • Duplex: IDuplexChannel • Sessionful variants also exist • Interfaces have common type hierarchy
Shapes and Interfaces public interface IInputChannel : IChannel, ICommunicationObject { Message Receive(); // standard Begin / End methods also exist (and timeouts)... } public interface IOutputChannel : IChannel, ICommunicationObject { void Send(Message msg); // standard Begin / End methods also exist (and timeouts)... } public interface IDuplexChannel : IInputChannel, IOutputChannel, IChannel, ICommunicationObject { } public interface IRequestChannel : IChannel, ICommunicationObject { Message Request(Message msg); // standard Begin / End methods also exist (and timeouts)... } // IReplyChannel omitted for clarity
ICommunicationObject • Channels and Channel Managers have a common state machine (also Faulted state) • Predictable state transitions
Agenda Bindings Channel Managers Channels Messages XmlWriters and XmlReaders Message Encoders Dispatcher Behaviors
The Message Type • Fundamental unit of communication • Channel layer interacts with Message objects • Seldom surfaces to the contract • CLR abstraction of a SOAP message • Body can be streamed, headers buffered • Can do non-SOAP formats (POX, JSON) public abstract class Message : IDisposable { public static Message CreateMessage(MessageVersion v, String a) {…} // lots of factory methods public void WriteMessage(XmlDictionaryWriter writer) {} // lots of write and read methods public abstract MessageHeaders Headers { get; } }
Envelope / Addressing Versions • EnvelopeVersionidentifies SOAP specs • 1.1, 1.2, none • AddressingVersionidentifies WS-Addressing • Aug 2004, 1.0, none • MessageVersionwraps both • None == no SOAP please
Agenda Bindings Channel Managers Channels Messages XmlWriters and XmlReaders Message Encoders Dispatcher and Behaviors
XmlDictionaryWriter • An abstract type derived from XmlWriter • Wraps a System.IO.Stream • Created via factory methods • Specialized for binary, MTOM, text, JSON MemoryStream stream = new MemoryStream(); XmlDictionaryWriterdictionaryWriter = XmlDictionaryWriter.CreateTextWriter(stream, Encoding.UTF8, false); dictionaryWriter.WriteElementString("localname", “http://namespace", "someValue"); dictionaryWriter.Flush();
XmlDictionaryReader • An abstract type derived from XmlReader • Wraps a System.IO.Stream or buffer • Created via factory methods • Specialized for binary, MTOM, text, JSON // Continued from previous slide stream.Position = 0; XmlDictionaryReader reader = XmlDictionaryReader.CreateTextReader(stream); reader.Read(); Console.WriteLine(reader.ReadOuterXml());
Encoding Sample - Text MemoryStream stream = new MemoryStream(); using (XmlDictionaryWriter writer = XmlDictionaryWriter.CreateTextWriter(stream, Encoding.UTF8, false)) { writer.WriteStartDocument(); writer.WriteElementString(“SongName”, “urn:ContosoRockabilia”, “Aqualung”); writer.Flush(); } // then read from Stream XmlDictionaryWriter (Text-UTF8) wrote 97 bytes 3C-3F-78-6D-6C-20-76-65-72-73-69-6F-6E-3D-22-31-2E- 30-22-20-65-6E-63-6F-64-69-6E-67-3D-22-75-74-66-2D- 38-22-3F-3E-3C-53-6F-6E-67-4E-61-6D-65-20-78-6D-6C- 6E-73-3D-22-75-72-6E-3A-43-6F-6E-74-6F-73-6F-52-6F- 63-6B-61-62-69-6C-69-61-22-3E-41-71-75-61-6C-75-6E- 67-3C-2F-53-6F-6E-67-4E-61-6D-65-3E data read from stream: <?xml version="1.0" encoding="utf-8"?> <SongNamexmlns="urn:ContosoRockabilia"> Aqualung </SongName>
Encoding Sample - Binary MemoryStream stream = new MemoryStream(); using (XmlDictionaryWriter writer = XmlDictionaryWriter.CreateBinaryWriter(stream, null, null)) { writer.WriteStartDocument(); writer.WriteElementString(“SongName”, “urn:ContosoRockabilia”, “Aqualung”); writer.Flush(); } // then read from Stream XmlDictionaryWriter (Binary) wrote 43 bytes 3F-08-53-6F-6E-67-4E-61-6D-65-04-15-75-72-6E-3A-43- 6F-6E-74-6F-73-6F-52-6F-63-6B-61-62-69-6C-69-61-A1- 08-41-71-75-61-6C-75-6E-67 data read from stream: N/A
Agenda Bindings Channel Managers Channels Messages XmlWriters and XmlReaders Message Encoders Dispatcher and Behaviors
From Message to the Wire • Known as Encoding • Via MessageEncoder and XmlWriter • MessageEncoder use Message.WriteMessage • Message.WriteMessage uses XmlWriter public class MyEncoder : MessageEncoder { public override void WriteMessage(Message msg, Stream stream){ XmlWriter writer = XmlWriter.CreateWriter(stream); msg.WriteMessage(writer); writer.Flush(); } // other members omitted }
From the Wire to Message • Known as Decoding • Done via ReadMessage • uses one of the Message.CreateMessage overloads • CreateMessage overload accepts an XmlReader public class MyEncoder : MessageEncoder { public override Message ReadMessage(Stream st, Int32 s, String ct){ XmlReader reader= XmlReader.Create(st); MessageVersion version = MessageVersion.Soap12WSAddressing10; return Message.CreateMessage(reader, s, version); } // other members omitted }
Walkthrough: Message-Wire-Message Message Channel Channel Message Encoder Message Encoder WriteMessage ReadMessage Message XmlWriter Bytes XmlReader Message Bytes Receiver Sender
Agenda Bindings Channel Managers Channels Messages XmlWriters and XmlReaders Message Encoders Dispatcher and Behaviors
About the Dispatcher • In the abstract, the Dispatcher hides from the receiver the fact that the receiving application is receiving messages • The Dispatcher is the realm of the contract • Messages go in, Parameters come out, and a method is invoked Receiver Receiving Application Parameters Dispatcher Protocol / Shaping / Transport Channels
Method2(…) Operation Invoker DispatchOperation Message Formatting Service Behaviors Operation Selector Message Inspection Extending the Dispatcher Overview Method1(…) Operation Behaviors DispatchOperation Parameter Inspection DispatchRuntime Channel
Important Types for Extending the Dispatcher • To extend the Dispatcher, define a type that implements one of the following • IOperationInvoker • Invokes method on target object • IDispatchMessageFormatter • to control format of the Message • IDispatchOperationSelector • to select operation on target object • IDispatchMessageInspector • to inspect the Message • IParameterInspector
Steps to Extend the Dispatcher • Define a type that implements one of the previous interfaces • Define a type that implements one of the Behavior interfaces and instantiates the type in Step # 1 • Add the Behavior from Step #2 to the description via the appropriate place in the ServiceDescription • OPTIONAL – add configuration support • OPTIONAL – add support for attribute annotation
IOperationInvoker • Enables control over the invocation of a method on the receiving object public interface IOperationInvoker { bool IsSynchronous { get; } object[] AllocateInputs(); object Invoke(object instance, object[] inpts, out object[] outputs); IAsyncResult InvokeBegin(object instance, object[] inpts, AsyncCallback cbck, object state); object InvokeEnd(object instance, out object[] outputs, IAsyncResult result); }
IDispatchMessageFormatter • Enables control over received Message deserialization and outbound Message serialization • SerializeReply returns a serialized Message object • DeserializeRequest returns an Object deserialized from the Message • MessageVersion fed from the Binding • SerializeReply is not invoked if operation is OneWay public interface IDispatchMessageFormatter { void DeserializeRequest(Message msg, object[] parameters); Message SerializeReply(MessageVersion version, object[] parameters, object result); }
IDispatchOperationSelector • Enables the selection of a particular DispatchOperation • SelectOperation returns the name of the method associated with the DispatchOperation • Normally loaded from the ContractDescription public interface IDispatchOperationSelector { String SelectOperation(ref Message message); }
IDispatchMessageInspector • Enables a last view of the Message before it is sent into the Channel stack and at the point when the reply surfaces • Notice that the Message is passed by reference • Be careful of Message state transitions • Request and Reply messages can be correlated manually • If the operation is OneWay, BeforeReceiveReply not invoked public interface IDispatchMessageInspector { Object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContextinstanceContext); void BeforeSendReply(ref Message reply, object correlationState); }
IParameterInspector • Enables the inspection of parameters before the request is deserialized or the reply is serialized • BeforeCall invoked before the Message is deserialized into parameters • AfterCall invoked after the reply is sent • If the operation is a OneWay operation, then AfterCall is not invoked • BeforeCall and AfterCall can be correlated via an Object public interface IParameterInspector { void AfterCall(String operationName, Object[] outputs, Object returnValue, Object correlationState); Object BeforeCall(String operationName, Object[] inputs); }
What Order Are Interceptors Invoked? IDispatchOperationSelector IDispatchMessageInspector IOperationInvoker IDispatchMessageFormatter IParameterInspector Sender IOperationInvoker IParameterInspector IDispatchMessageFormatter IDispatchMessageInspector
Behaviors demo