con 413 justin smith technical evangelist
TRANSCRIPT
WCF Architecture and Extensibility Points
CON 413
Justin SmithTechnical Evangelisthttp://blogs.msdn.com/justinjsmith
Agenda
BindingsChannel ManagersChannelsMessagesXmlWriters and XmlReadersMessage EncodersDispatcher and Behaviors
Receiver
Channels
WCF Architecture: Layers of Layers
Sender
Proxy
Sender Application
Dispatcher
Receiver Application
Channel Layer
ServiceModel Layer
Channels
Agenda
BindingsChannel ManagersChannelsMessagesXmlWriters and XmlReadersMessage EncodersDispatcher and Behaviors
What is a Binding?
Abstraction of messaging functionalityTransport, security, transaction options, protocols
Creates a collection of BindingElement objects
Arrangement in collection is importantNetTcpBinding b = new NetTcpBinding();foreach (BindingElement el in b.CreateBindingElements()) { Console.WriteLine(el.GetType().Name);}
// outputsTransactionFlowBindingElement // protocolBinaryMessageEncodingBindingElement // encodingWindowsStreamSecurityBindingElement // securityTcpTransportBindingElement // transport
What is a BindingElement?
Create Channel Listeners & Channel Factories
listeners on receiver / factories on sendercollectively known as Channel Managers
Collection creates a stack (listeners/factories)
via BindingContext and CustomBinding typesBindingContext keeps a mutable list of BindingElement objects in CollectionCustomBinding is isomorphic
Channel Factory Stack
BindingContext
CustomBinding
Walkthrough : Channel Factory
NetTcpBinding
TransactionFlowBindingElementBinaryMessageEncodingBindingElem
entWindowsStreamSecurityBindingElem
entTcpTransportBindingElement
BuildChannelFactory<T>
Bu
ildIn
nerC
han
nelF
act
ory
<T
>
TcpChannelFactory<T>
TransactionFlowChannelFactory<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
BindingsChannel ManagersChannelsMessagesXmlWriters and XmlReadersMessage EncodersDispatcher and Behaviors
What is a Channel Factory?
Create sending ChannelsStack of Channel Factories creates Channel stack
Channel Factory stack is opaqueGetProperty<T> allows some query capabilities
Each Channel Factory in stack:Refers to the next Channel FactoryDelegates GetProperty<T> calls when unknown
EndpointAddress needed in channel stackUri for Via can be used as well
What is a Channel Listener?
Listens for incoming messages & Creates receiving channel stacksBottom-most Channel Listener listens on transport
Embrace “Accept” semantics similar to sockets“Accept” returns a
Channel Listener stacks are opaqueSymmetry with Channel Factory design
Channel Stack
CreateChannel
Channel Factory Stack
Walkthrough : Channel Stack
TcpChannelFactory<T>
TransactionChannelFactory<T>
CreateChannel
TcpChannel<T>
TransactionChannel<T>
From a Channel Factory:
From a Channel Listener:
Channel Listener Stack
TcpChannelListener
TransactionChannelListener
AcceptChannel
Channel Stack
TcpChannel<T>
TransactionChannel<T>AcceptChannel
message passing to channel stack not precise
Agenda
BindingsChannel ManagersChannelsMessagesXmlWriters and XmlReadersMessage EncodersDispatcher and Behaviors
What is a Channel?
Sends and/or receives Message objectsFor specific transport or protocol functionalityCommonly stacked w / other Channel objectsHave shape described by interfaces
MEP (datagram, request/reply, duplex)Composition of channel stack dictates messaging functionality
Consider WS-RM
Sender Receiver
CreateSequence
CreateSequenceResponse (ID)
XSequence (ID, Msg #2)
Sequence (ID, Msg #1)
Sequence (ID, Msg #3, LastMessage)
SequenceAck (ID, Msg #1, Msg #3)
Sequence (ID, Msg #2, AckRequested)
SequenceAck (ID, Msg #1 - Msg #3)
TerminateSequence (ID)
Sending Application
Channel Stack
Receiving Application
Sequence (#2, LastMessage)
CreateSequence / CreateSequenceRespons
eSequence (#1)Ack (Msg #1-2)
Client
Walkthrough: WS-RM
HttpRequestChannel<T>
ReliableOutputSessionChannelTerminateSequence
MEPs and The Channel Layer
Channel Managers and Channels have shapeShape represents the supported MEP(s)
Datagram, Request/Reply, DuplexWCF represents shape via interfaces
Datagram: IInputChannel, IOutputChannelRequest / Reply: IRequestChannel, IReplyChannelDuplex: IDuplexChannelSessionful variants also exist
Interfaces have common type hierarchy
Shapes and Interfacespublic 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
Created Opening Opened Closing Closed
Abort()
Abort()
Open() Close()
Agenda
BindingsChannel ManagersChannelsMessagesXmlWriters and XmlReadersMessage EncodersDispatcherBehaviors
The Message Type
Fundamental unit of communicationChannel layer interacts with Message objectsSeldom surfaces to the contract
CLR abstraction of a SOAP messageBody 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
EnvelopeVersion identifies SOAP specs1.1, 1.2, none
AddressingVersion identifies WS-Addressing
Aug 2004, 1.0, noneMessageVersion wraps bothNone == no SOAP please
Agenda
BindingsChannel ManagersChannelsMessagesXmlWriters and XmlReadersMessage EncodersDispatcher and Behaviors
XmlDictionaryWriter
An abstract type derived from XmlWriter
Wraps a System.IO.StreamCreated via factory methods
Specialized for binary, MTOM, text, JSON
MemoryStream stream = new MemoryStream();XmlDictionaryWriter dictionaryWriter = 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 bufferCreated via factory methods
Specialized for binary, MTOM, text, JSON
// Continued from previous slidestream.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 bytes3C-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"?><SongName xmlns="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 bytes3F-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
BindingsChannel ManagersChannelsMessagesXmlWriters and XmlReadersMessage EncodersDispatcher and Behaviors
From Message to the Wire
Known as EncodingVia MessageEncoder and XmlWriter
MessageEncoder use Message.WriteMessageMessage.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 DecodingDone via ReadMessage
uses one of the Message.CreateMessage overloadsCreateMessage 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}
Channel
Message Encoder
Channel
Message Encoder
Wri
teM
ess
ag
e
Walkthrough: Message-Wire-Message
Sender Receiver
Message
XmlWriter
Message
ReadM
ess
ag
e XmlReaderBytes
Bytes
Message
Agenda
BindingsChannel ManagersChannelsMessagesXmlWriters and XmlReadersMessage EncodersDispatcher and Behaviors
About the Dispatcher
In the abstract, the Dispatcher hides from the receiver the fact that the receiving application is receiving messagesThe Dispatcher is the realm of the contractMessages go in, Parameters come out, and a method is invoked
Receiver
Dispatcher
Receiving Application
Protocol / Shaping / Transport Channels
Parameters
Extending the Dispatcher Overview
Message Inspection
Service BehaviorsOperation Selector
Message Formatting
Operation Invoker
Channel
Method1(…)
DispatchOperation
Method2(…)
DispatchOperation
DispatchRuntime
Parameter Inspection
Operation Behaviors
Important Types for Extending the Dispatcher
To extend the Dispatcher, define a type that implements one of the followingIOperationInvoker
Invokes method on target objectIDispatchMessageFormatter
to control format of the MessageIDispatchOperationSelector
to select operation on target objectIDispatchMessageInspector
to inspect the MessageIParameterInspector
Steps to Extend the Dispatcher1. Define a type that implements one of the
previous interfaces2. Define a type that implements one of the
Behavior interfaces and instantiates the type in Step # 1
3. Add the Behavior from Step #2 to the description via the appropriate place in the ServiceDescription
4. OPTIONAL – add configuration support5. 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 serializationSerializeReply returns a serialized Message objectDeserializeRequest returns an Object deserialized from the MessageMessageVersion fed from the BindingSerializeReply 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 DispatchOperationSelectOperation 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 surfacesNotice that the Message is passed by reference
Be careful of Message state transitionsRequest and Reply messages can be correlated manuallyIf the operation is OneWay, BeforeReceiveReply not invoked
public interface IDispatchMessageInspector { Object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext);
void BeforeSendReply(ref Message reply, object correlationState);}
IParameterInspector
Enables the inspection of parameters before the request is deserialized or the reply is serializedBeforeCall invoked before the Message is deserialized into parametersAfterCall 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
IDispatchMessageInspector
IDispatchMessageFormatter
IParameterInspector
Sender
IParameterInspector
IOperationInvoker