WCF Architecture and Extensibility P oints
description
Transcript of WCF Architecture and Extensibility P oints
WCF Architecture and Extensibility Points
CON 413
Justin SmithTechnical Evangelisthttp://blogs.msdn.com/justinjsmith
AgendaBindingsChannel 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
AgendaBindingsChannel ManagersChannelsMessagesXmlWriters and XmlReadersMessage EncodersDispatcher 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 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 StackBindingContext
CustomBinding
Walkthrough : Channel Factory
NetTcpBinding
TransactionFlowBindingElementBinaryMessageEncodingBindingElem
entWindowsStreamSecurityBindingElement
TcpTransportBindingElement
BuildChannelFactory<T>
Build
Inne
rCha
nnel
Fact
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}
AgendaBindingsChannel ManagersChannelsMessagesXmlWriters and XmlReadersMessage EncodersDispatcher and Behaviors
What is a Channel Factory?Create sending Channels
Stack of Channel Factories creates Channel stackChannel Factory stack is opaque
GetProperty<T> allows some query capabilitiesEach 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 StackTcpChannelListener
TransactionChannelListener
AcceptChannelChannel StackTcpChannel<T>
TransactionChannel<T>AcceptChannel
message passing to channel stack not precise
AgendaBindingsChannel 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-RMSender Receiver
CreateSequenceCreateSequenceResponse (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>
ReliableOutputSessionChannel TerminateSequence
MEPs and The Channel LayerChannel 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
ICommunicationObjectChannels and Channel Managers have a common state machine (also Faulted state)
Predictable state transitions
Created Opening Opened Closing Closed
Abort()
Abort()
Open() Close()
AgendaBindingsChannel ManagersChannelsMessagesXmlWriters and XmlReadersMessage EncodersDispatcherBehaviors
The Message TypeFundamental unit of communication
Channel 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
AgendaBindingsChannel ManagersChannelsMessagesXmlWriters and XmlReadersMessage EncodersDispatcher and Behaviors
XmlDictionaryWriterAn abstract type derived from XmlWriter
Wraps a System.IO.StreamCreated via factory methods
Specialized for binary, MTOM, text, JSONMemoryStream stream = new MemoryStream();XmlDictionaryWriter dictionaryWriter = XmlDictionaryWriter.CreateTextWriter(stream, Encoding.UTF8, false);dictionaryWriter.WriteElementString("localname", “http://namespace", "someValue");dictionaryWriter.Flush();
XmlDictionaryReaderAn 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 - BinaryMemoryStream 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
AgendaBindingsChannel 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
Writ
eMes
sag
e
Walkthrough: Message-Wire-Message
Sender Receiver
Message
XmlWriterMessage
Read
Mes
sag
e XmlReaderBytesBytes
Message
Channel Layer Service
demo
AgendaBindingsChannel ManagersChannelsMessagesXmlWriters and XmlReadersMessage EncodersDispatcher and Behaviors
About the DispatcherIn 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
IOperationInvokerEnables 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);}
IDispatchMessageFormatterEnables 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);}
IDispatchOperationSelectorEnables 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); }
IDispatchMessageInspectorEnables 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);}
IParameterInspectorEnables 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
Behaviors
demo
Evaluation Forms
Questions?