Click here to Skip to main content
Click here to Skip to main content

NullTransport for WCF

, 1 Oct 2007
Rate this:
Please Sign up or sign in to vote.
This article describes design, implementation and the usage of the custom in-process transport for Microsoft Windows Communication Foundation (WCF) model.

Contents

Note: The Workflow test case requires installation of the .NET Framework 3.5 (Orcas) Beta 2 version.

Features

  • Input/Output (One way)
  • Request/Reply
  • Duplex
  • Session
  • Transactional
  • Sync and Async connectivity fashions
  • No Encoder

Introduction

The Microsoft Windows Communication Foundation (WCF) represents a logical model of connectivity, where the connectivity between the service and its consumer is abstracted to the stack of the channels on top of the physical transport layer. Based on the binding stack elements and physical transport on both ends of the connectivity, we can define how the message can flow between the business layers in terms of the message exchange pattern (MEP). Note, the business layers do not need to know about each other, such as where they are hosted, located and connected, therefore we can say, the business layers are logical connected.

The business connected layers represented by the client (proxy) and service can live in the same appDomain or on the different appDomains in the same process, out of the process or across the machine. Having the logical connectivity model in the application architecture enables it to encapsulate the business layers from the connectivity driven by the metadata.

The WCF model introduces several common transports and bindings to create a sync and async connectivity with a variant of the message exchange patterns such as Input/Output, Request/Reply, Session, Duplex, etc. over the different physical transports such as TCP, HTTP, MSMQ, Pipe, etc. It is a great open connectivity paradigm with the ability to customize and extend all elements based on the business needs.

Based on the physical transport, the business can live in the same tier or can be decoupled across an Enterprise Network using the principle of the Service Oriented Architecture (SOA) tenets.

This article is focused on the WCF connectivity model within the same appDomain. The current version of the .netfx 3.0 and also upcoming version 3.5 (Orcas) uses the named pipe for in-process communication between the service and its consumer.

The following picture shows a netNamedPipeBinding WCF connectivity:

The logical connectivity between the business layers is mapped to the WCF model described by their service contract. This description represents the metadata of the connectivity such as Address, Binding and Contract (ABC). Basically, the client and service using the same channel stack banded by Protocol, Encoder and Transport layers. The client channel is connected to the business layer by Proxy (using the transparent proxy pattern) and on the service side, the Dispatch will invoke a service operation method. More details about this great communication paradigm can be found in the MSDN documentation and .NET forum. I assume that you already read the documentation and have some working knowledge and experience with WCF programming.

As I mentioned earlier, this article focuses on the in-process communication, therefore let's continue with this focus. When the client invokes the service operation on the transparent CLR proxy, the operation is mapped into the message object by the Protocol layer (depends on the message exchange pattern) and passing the message to the Encoder for its serialization.

The Transport layer will take this binary stream or formatted text over the appDomain using a specific physical media, in our case it is a local Named Pipe running in the kernel mode, which can be reachable by any process on the same machine. The service's Encoder must decode the incoming stream/text into the message object and pass it to the Protocol channels. At the end, the Dispatch's Operation Invoker will invoke a method on the service instance.

So far so good, the netNamedPipeBinding allows to connect and transport binary message object using the local named pipe resource via a kernel mode between the service and client in a fast and reliable sync/async manner. So, what is the point?

The point is described in the following picture; if the client and service are located in the same appDomain. Do we need to go out and back into the same appDomain to handle the internal domain communications via inter-process kernel resources?

The following picture can answer for in-process connectivity:

As you can see, the physical layer of the connectivity represented by NULL Transport will take a clone message object and pass it directly to the service protocol layer. This logic is similar to the router, when the output channel is routing a message to the correct Input channel. Note, this NULL connectivity is fully transparent between the service and client and there is no restriction on the service to have multiple endpoints with a different ABC for binding.

For example: The service with two endpoints, let's say the first one is netTcpBinding and the other one a custom binding with the Null Transport can handle the public and private connectivity very efficiently.

Another example of the NullTransport usage is the incoming netfx 3.5 version (currently in beta2), where WCF and WF models are integrated into one common WorkflowService model. This model via new Receive and Send Activities enables to communicate with workflow based on the service contract in the context manner. The business encapsulated into the type/XOML workflows can be orchestrated in the logical model based on the connectivity metadata without the knowledge where they are physically hosted.

The following picture shows the connectivity within the appDomain for WCF and WorkflowServices. In this case, the services and clients are logically connected to the appDomain bus represented by the NULL Transport layer:

The WorkflowServices are a great step into the distributed workflow model, where a workflow can invoke another workflow in the context message exchange pattern on the internal or public Enterprise Service Bus.

That's all for the introduction of the Null Transport, I hope you captured having this transport in-process communication model when the correct business layers in the tier can talk to each other using the same communication model as between the tiers.

I assume you have knowledge of WCF and its extensibility; therefore I will focus more on the concept and implementation of the transport layer than on the hierarchy of the custom built channels. Ok, let's start with the concept and follow up its implementation.

Concept and Implementation

The concept of the NullTransport is based on the routing (dispatching) the cloned output message object to the specific listener based on the EndpointAddressMessageFilter data. Each opened listener will subscribe its EndpointAddressMessageFilter into the MessageFilterTable stored in the process data slot. Based on this key, the MessageFilterTable will return a reference to the listener and its private InputQueueChannel for enqueueing the message object. Once the message arrives into the input async queue, the channel will process it like the message generated by Encoder layer.

The following picture shows the concept of the NullTransport with four listeners related to the MEP:

When the Listener is constructed, the EndpointAddressMessageFilter is created for full local endpoint address and later it will be used in the AcceptChannel process.

The following code snippet shows these parts of the NullInputChannelListener implementation:

public NullInputChannelListener(NullTransportBindingElement element, 
    BindingContext context): base(context.Binding)
{
    _element = element;
    _context = context;
  
    Uri listenUri = new Uri(context.ListenUriBaseAddress, 
        context.ListenUriRelativeAddress);
    _localAddress = new EndpointAddress(listenUri);
    _filter = new EndpointAddressMessageFilter(_localAddress);
}

protected override IInputChannel OnAcceptChannel(TimeSpan timeout)
{
    if (base.State == CommunicationState.Opened)
    {
        if (_currentChannel != null)
        { 
            // we are supporting only one channel in the listener
            _waitChannel.WaitOne(int.MaxValue, true);

            lock (ThisLock)
            {
                // re-open channel
                if (_currentChannel.State == CommunicationState.Closed &&
                base.State == CommunicationState.Opened)
                {
                    _currentChannel = 
                        new NullInputChannel(this, _localAddress);
                    _currentChannel.Closed += 
                        new EventHandler(OnCurrentChannelClosed);
                }
            }
        }
        else
        {
            lock (ThisLock)
            {
                // open channel at first time
                _currentChannel = new NullInputChannel(this, _localAddress);
                _currentChannel.Closed += 
                    new EventHandler(OnCurrentChannelClosed);
                NullListeners.Current.Add(_filter, this);
            }
        }
    }
    return _currentChannel;
}

The NullListener.Current is a static method to subscribe/unsubscribe a specific Listener to the MessageFilterTable and dispatch a message to the Listener's InputQueueChannel.

Once the Listener has been subscribed, the message can be dispatched. The following code snippet shows sending a message from the OutputChannel:

public void Send(Message message, TimeSpan timeout)
{
    // double check
    base.ThrowIfDisposedOrNotOpen();

    // add remote address to the message header
    base.RemoteAddress.ApplyTo(message);

    // create buffered copy
    MessageBuffer buffer = message.CreateBufferedCopy(int.MaxValue);
    Message message2 = buffer.CreateMessage();

    // dispatch to the listener
    ThreadPool.QueueUserWorkItem
        (new WaitCallback(this.DispatchToListener), message2); 
}

protected virtual void DispatchToListener(object state)
{
    try
    {
        NullListeners.Current.Dispatch<NullInputChannelListener>
            (state as Message);
    }
    catch (Exception ex)
    {
        (state as Message).Close();
    }
}

As you can see, the cloned message is sent to the dispatcher in an async manner without waiting for its response. This is the design for fire & forget message exchange pattern represented by the Input and Output channel.

Building the custom WCF channel/transport is a straightforward task divided into the several layers with the interface plumbing patterns and some common behavior located in the CommunicationObject base class.

The following picture shows a basic boilerplate for custom NullChannels for message exchange patterns such as Input/Output, InputSession/OutputSession, Request/Reply and RequestSession/ReplySession:

The first part of this boilerplate is related to the plug-in of the custom transport to the binding collection. The NullTransportElement must be added into the <bindingElementExtensions> element in the config file or programmatically done prior of the usage of this custom binding. Once we have our custom binding in the extensions, it can be recognized by endpoint and can call the plumbing pattern like is shown in the above picture.

The NullTransportBindingElement handles building the channels for Listener (Service) and Factory (Proxy) Channels based on the service contract. For instance: if the service contract has a one way operation, the Input/Output message exchange pattern will be applied for building the channels.

The following picture shows a class diagram for NullChannels. These channels are initiated by Factory and Listener layers:

The simple MEP, such as one directional sending a message to the input channel can be completed directly by enqueueing of the cloned message object into the input channel queue in the fire & forget manner - see the above code snippets. The plumbing layers are represented by IOuputChannel and IInputChannel patterns.

To receive a response on the request described in the Request/Reply MEP, the plumbing layers must be driven by IRequestChannel and IReplyChannel interfaces and RequestContext object.

The RequestContext object represents a "vehicle" for handling a message object between the request and reply tasks for their synchronization and message passing. The following picture shows an implementation of the RequestContext abstract class for NullTransport:

The NullAsyncRequestContext object is designed for plumbing inner's channel layers such as NullRequestChannel and NullReplyChannel respectively NullRequestSessionChannel and NullReplySessionChannel into the WCF channel protocol. The RequestContext object can be handled by both sides asynchronously or synchronously using the blocking manner.

The following code snippet shows the Request implementation in the NullRequestChannel:

public IAsyncResult BeginRequest(Message message, 
    AsyncCallback callback, object state)
{
    return this.BeginRequest
        (message, base.DefaultSendTimeout, callback, state);
}

public IAsyncResult BeginRequest(Message message, TimeSpan timeout, 
    AsyncCallback callback, object state)
{
    base.ThrowIfDisposedOrNotOpen();
    base.RemoteAddress.ApplyTo(message);

    NullAsyncRequestContext request = 
        new NullAsyncRequestContext(message,timeout,callback,state);
    this.DispatchToListener(request);
    base.PendingRequests.Add(request);
    return request;
}  
   
public Message EndRequest(IAsyncResult result)
{
    return NullAsyncRequestContext.End(result);
}

public Message Request(Message message)
{
    return this.Request(message, base.DefaultSendTimeout);
}

public virtual Message Request(Message message, TimeSpan timeout)
{
    base.ThrowIfDisposedOrNotOpen();
    base.RemoteAddress.ApplyTo(message);

    NullAsyncRequestContext request = 
        new NullAsyncRequestContext(message, timeout);
    base.PendingRequests.Add(request);

    try
    {
        ThreadPool.QueueUserWorkItem
            (new WaitCallback(this.DispatchToListener), request); 
        return request.WaitForReply();
    }
    finally
    {
        base.PendingRequests.Close(request);
    }
}

protected virtual void DispatchToListener(object state)
{
    try
    {
        NullListeners.Current.Dispatch<NullReplyChannelListener>
            (state as NullAsyncRequestContext);
    }
    catch (Exception ex)
    {
        (state as NullAsyncRequestContext).Abort(ex);
    }
}

In the case of BeginRequest, the method will create a RequestContext object with async arguments that are dispatched to the specific ReplyChannel without waiting for its reply. When the service operation is finished, the reply message is sent to the RequestContext by invoking the following method:

public override void Reply(Message message, TimeSpan timeout)
{
    lock (ThisLock)
    {
        ThrowIfInvalidReply();

        if (message != null)
        {
            MessageBuffer buffer = message.CreateBufferedCopy(int.MaxValue);
            _response = buffer.CreateMessage();
        }
        else
        {
            _response = null;
        }
        _replySent = true;
    
        if (_waitForReply != null)
        {
            _waitForReply.Set();
        }
    }

    if (_callback != null)
    {
        // call callback for reply is done
        _callback(this);
    }
}

The responsibility of the above method is to clone the message object and invoke an async callback to signal that the operation is done and the response is ready in the RequestContext object. This process is completed by the EndRequest method, where the reply message is returned to the requester (NullRequestChannel with an anonymous ReplyTo address).

The following picture shows a request and reply message captured by MessageInspector for <reliableSession> over <nullTransport> binding:

Test

The following picture shows the solutions for NULL Transport implementation including test projects for .netfx 3.0 and 3.5 versions. Note, the solution for .netfx 3.5 is only for the WorkflowService test case and it requires the installation of .netfx 3.5 version (currently in beta 2). This solution references to the NullChannelLib.dll assembly built under the netfx 3.0 version.

As you can see, the NullChannelLib project contains several files located in the folders based on communication layers and MEPs.

For test purposes, each test console application has its own config file. The following picture shows an example of the configuration for nullTransport custom binding. Note, the bindingElementExtension is required to use this transport.

The message exchange patterns via the custom NULL Transport can be tested individually using the test projects such as Test_Duplex, Test_Session, Test_Tx (Transaction) and Test Workflow. The solution also contains a Logger for displaying WCF messages on the console screen.

Note, the config file contains a diagnostics configuration for tracing the messages across the WCF model. Please find the patch for this tracing in the config file and use the \Microsoft SDKs\Windows\V6.1\Bin\SvcTraceViewer.exe utility to display the trace log messages.

Test_Workflow

I selected this test to describe the capability of the NULL Transport within the same AppDomain where the WCF model is used to connect the business objects located in the different layers. The console application represents a UI and host process for the business objects located in the workflows and service. Using the configuration file, the layers can be connected via the custom net.null transport or others, based on the application needs.

The first ReceiveActivity in the WorfklowService enables the creation of the workflow instance and its instanceId will flow in the context during the valid session, therefore the second request message from the console program will be dispatched during the next ReceiveActivity.

The following picture shows a property of the first ReceiveActivity:

The purpose of the following picture is shown in the message exchange with a first ReceiveActivity. The Request/Reply message exchange pattern is demonstrated by passing the context header with a workflow instance id back to the caller. In addition, the screen shows workflow runtime events such as created, started, idled and persisted:

Note, the WorkflowService will automatically create a workflow instance id for you. There is no option in the current version of the netfx 3.5 to pass a context with a specific id in the first request call to the WorkflowService. However, I implemented a small workaround (CreateWorkflowAttribute.cs) for this issue based on the custom operation attribute [CreateWorkflow] - see the following code snippet:

[ServiceContract(SessionMode = SessionMode.Required)]
public interface ITest
{
    [OperationContract]
    [TransactionFlow(TransactionFlowOption.Allowed)]
    void Ping(int value);

    [OperationContract(IsInitiating = true)]
    [TransactionFlow(TransactionFlowOption.Allowed)]
    [CreateWorkflow]
    string Ping2(int value);
}

Now, the client can create the instanceId and pass it via the request message to the WorkflowService. The following code snippet shows an example of creating a context header for an outgoing message:

using (OperationContextScope scope = 
    new OperationContextScope((IContextChannel)channel))
{
    string instanceId = Guid.NewGuid().ToString();
  
    Dictionary<XmlQualifiedName, string> dic = 
        new Dictionary<XmlQualifiedName, string>();
  
    dic.Add(new XmlQualifiedName("InstanceId", 
        "http://schemas.microsoft.com/ws/2006/05/context"), instanceId);
  
    ContextMessageProperty mp = new ContextMessageProperty(dic);
    OperationContext.Current.OutgoingMessageProperties.Add
        ("ContextMessageProperty", mp);

    string response = channel.Ping2(loop);

    // ...
}

and the outgoing request message will look as it is shown in the following code snippet:

<s:Envelope 
  xmlns:a="http://www.w3.org/2005/08/addressing" 
  xmlns:s="http://www.w3.org/2003/05/soap-envelope">
  <s:Header>
    <a:Action s:mustUnderstand="1">http://tempuri.org/ITest/Ping2</a:Action>
    <a:MessageID>urn:uuid:ce8e478c-ab6a-401d-958b-d1d3de2f972d</a:MessageID>
    <a:ReplyTo>
      <a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
    </a:ReplyTo>
    <Context xmlns="http://schemas.microsoft.com/ws/2006/05/context">
      <InstanceId>044a3c30-3558-47de-8647-2c0d9b4f9359</InstanceId>
    </Context>
    <a:To s:mustUnderstand="1">net.null://localhost/gaga</a:To>
  </s:Header>
  <s:Body>
    <Ping2 xmlns="http://tempuri.org/">
      <value>0</value>
    </Ping2>
  </s:Body>
</s:Envelope>

Remoting Vs. namedPipeTransport Vs. nullTransport

The fast standard transport from WCF model is via namedPipeTransport using the local named pipe running in kernel mode. As I mentioned earlier, the message must be encoded via this transport. The Test_Session program can be used to compare the performance between the named pipe and null transport (after a small update in the config file related to the namedPipeTransport is completed).

On my Dell multiprocessors (4 cores) machine, the null transport is faster than pipe in the score of 2.462 ms to 3.062 ms. Also, I built a similar test for remoting object and the result showed me that the null transport is approximately 8% faster than TCP remoting channel. Note, the test was done for a very small message body - see the ITest contract. In the case of large messages, the null transport will perform better because there is no encoder layer in the channel.

Conclusion

This article described the in-process of the WCF Transport without using the Encoder layer. This efficient binding enables the usage of a common communication WCF paradigm for connectivity between the business layers located in the same appDomain. Using the config file; the connectivity can be easily changed administratively for crossing the boundary. The upcoming new .netfx 3.5 version related to the common model for connected systems with a workflow behind the service is a suitable candidate for NullTransport binding, where the business model can be logically run in one or more processes based on business needs.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Roman Kiss
Software Developer (Senior)
United States United States
No Biography provided

Comments and Discussions

 
GeneralMy vote of 4 Pinmembersthotakura20-May-13 4:22 
QuestionHost in IIS Pinmembernickkukolev1-Mar-13 23:47 
QuestionWhat can be done with performance? [modified] PinmemberDzmitry Lahoda19-Oct-12 1:40 
AnswerRe: What can be done with performance? PinmemberDzmitry Lahoda15-Dec-12 22:57 
GeneralMy vote of 5 Pinmemberphoenicyan24-Jan-11 6:53 
Generalcompilation error in VS2010 PinmemberKonstantin Izmailov24-Jan-11 6:48 
Generalperfomance Pinmemberjury_mart19-Aug-10 2:45 
GeneralGreat Article Pinmemberbender756-Dec-09 21:35 
Questionsecurity capabilities problem PinmemberDeyan Petrov12-May-09 23:28 
AnswerRe: security capabilities problem PinmemberGoffkock7-Dec-09 4:02 
GeneralRe: security capabilities problem PinmemberDeyan Petrov7-Dec-09 4:59 
GeneralRe: security capabilities problem PinmemberGoffkock7-Dec-09 5:41 
GeneralRe: security capabilities problem PinmemberDeyan Petrov7-Dec-09 5:50 
Well, can't find your email address, and don't see a possibility to attach files here, so I paste directly here:
 
NullTransportBindingElement.cs
 
//*****************************************************************************
//    Description.....Null Transport 
//                                
//    Author..........Roman Kiss, rkiss@pathcom.com
//    Copyright © 2007 ATZ Consulting Inc.  (see included license.rtf file)     
//                        
//    Date Created:    07/07/07
//
//    Date        Modified By     Description
//-----------------------------------------------------------------------------
//    07/07/07    Roman Kiss     Initial Revision
//*****************************************************************************

using System;
using System.Net.Security;
using System.ServiceModel.Channels;
using Framework.CommunicationFoundation.Channels;
using Framework.CommunicationFoundation.Channels.Listeners;
 
namespace Framework.CommunicationFoundation.Bindings
{
	/// <summary>
	/// NullTransportBindingElement
	/// </summary>
	public class NullTransportBindingElement : TransportBindingElement
	{
		private bool _bindingContainsSecurityElement = false;
 
		#region Properties
 
		/// <summary>
		/// Gets or sets the supported request protection level.
		/// </summary>
		/// <value>The supported request protection level.</value>
		public ProtectionLevel SupportedRequestProtectionLevel { get; set; }
 
		/// <summary>
		/// Gets or sets the supported response protection level.
		/// </summary>
		/// <value>The supported response protection level.</value>
		public ProtectionLevel SupportedResponseProtectionLevel { get; set; }
 
		/// <summary>
		/// Gets or sets a value indicating whether [supports client authentication].
		/// </summary>
		/// <value>
		/// 	<c>true</c> if [supports client authentication]; otherwise, <c>false</c>.
		/// </value>
		public bool SupportsClientAuthentication { get; set; }
 
		/// <summary>
		/// Gets or sets a value indicating whether [supports client windows identity].
		/// </summary>
		/// <value>
		/// 	<c>true</c> if [supports client windows identity]; otherwise, <c>false</c>.
		/// </value>
		public bool SupportsClientWindowsIdentity { get; set; }
 
		/// <summary>
		/// Gets or sets a value indicating whether [supports server authentication].
		/// </summary>
		/// <value>
		/// 	<c>true</c> if [supports server authentication]; otherwise, <c>false</c>.
		/// </value>
		public bool SupportsServerAuthentication { get; set; }
 
		#endregion
 
		/// <summary>
		/// Initializes a new instance of the <see cref="NullTransportBindingElement"/> class.
		/// </summary>
		public NullTransportBindingElement()
		{
		}
 
		/// <summary>
		/// Initializes a new instance of the <see cref="NullTransportBindingElement"/> class.
		/// </summary>
		/// <param name="elementToBeCloned">The element to be cloned.</param>
		public NullTransportBindingElement(NullTransportBindingElement elementToBeCloned)
			: base(elementToBeCloned)
		{
			SupportedRequestProtectionLevel = elementToBeCloned.SupportedRequestProtectionLevel;
			SupportedResponseProtectionLevel = elementToBeCloned.SupportedResponseProtectionLevel;
			SupportsClientAuthentication = elementToBeCloned.SupportsClientAuthentication;
			SupportsClientWindowsIdentity = elementToBeCloned.SupportsClientWindowsIdentity;
			SupportsServerAuthentication = elementToBeCloned.SupportsServerAuthentication;
		}
 
		#region Overrides
 
		/// <summary>
		/// Gets the URI scheme for the transport.
		/// </summary>
		/// <value></value>
		/// <returns>Returns the URI scheme for the transport, which varies depending on what derived class implements this method.</returns>
		public override string Scheme
		{
			get { return "net.null"; }
		}
 
		/// <summary>
		/// Returns a copy of the binding element object.
		/// </summary>
		/// <returns>
		/// A <see cref="T:System.ServiceModel.Channels.BindingElement"/> object that is a deep clone of the original.
		/// </returns>
		public override BindingElement Clone()
		{
			return new NullTransportBindingElement(this);
		}
 
		/// <summary>
		/// Gets the property.
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="context">The context.</param>
		/// <returns></returns>
		public override T GetProperty<T>(BindingContext context)
		{
			if (context == null)
			{
				throw new ArgumentNullException("context");
			}
 
			//if(context.Binding.Elements.Contains(typeof(SecurityBindingElement)) && !_bindingContainsSecurityElement)
			//{
			//    _bindingContainsSecurityElement = true;
			//}

			//if (typeof(T) == typeof(ISecurityCapabilities) && _bindingContainsSecurityElement)
			if (typeof(T) == typeof(ISecurityCapabilities))
			{
				//if(_bindingContainsSecurityElement)
				//ISecurityCapabilities securityCapabilities = (ISecurityCapabilities)base.GetProperty<T>(context);

				////BindingParameterCollection parameters = new BindingParameterCollection();

				////securityCapabilities = context.Binding.GetProperty<ISecurityCapabilities>(parameters);
				////Debug.WriteLine(securityCapabilities.SupportedRequestProtectionLevel);
				////Debug.WriteLine(securityCapabilities.SupportedResponseProtectionLevel);
				////Debug.WriteLine(securityCapabilities.SupportsClientAuthentication);
				////Debug.WriteLine(securityCapabilities.SupportsClientWindowsIdentity);
				////Debug.WriteLine(securityCapabilities.SupportsServerAuthentication);

				//return (T)securityCapabilities;

				return (T)(object)new SupportedSecurityCapabilities(SupportedRequestProtectionLevel, 
					SupportedResponseProtectionLevel, SupportsClientAuthentication, SupportsClientWindowsIdentity, 
					SupportsServerAuthentication);
			}
 
			return base.GetProperty<T>(context);
		}
 
		/// <summary>
		/// Determines whether this instance [can build channel factory] the specified context.
		/// </summary>
		/// <typeparam name="TChannel">The type of the channel.</typeparam>
		/// <param name="context">The context.</param>
		/// <returns>
		/// 	<c>true</c> if this instance [can build channel factory] the specified context; otherwise, <c>false</c>.
		/// </returns>
		public override bool CanBuildChannelFactory<TChannel>(BindingContext context)
		{
			return
				typeof(TChannel) == typeof(IOutputChannel) ||
				typeof(TChannel) == typeof(IOutputSessionChannel) ||
				typeof(TChannel) == typeof(IRequestChannel) ||
				typeof(TChannel) == typeof(IRequestSessionChannel);
		}
 
		/// <summary>
		/// Determines whether this instance [can build channel listener] the specified context.
		/// </summary>
		/// <typeparam name="TChannel">The type of the channel.</typeparam>
		/// <param name="context">The context.</param>
		/// <returns>
		/// 	<c>true</c> if this instance [can build channel listener] the specified context; otherwise, <c>false</c>.
		/// </returns>
		public override bool CanBuildChannelListener<TChannel>(BindingContext context)
		{
			return
				typeof(TChannel) == typeof(IInputChannel) ||
				typeof(TChannel) == typeof(IInputSessionChannel) ||
				typeof(TChannel) == typeof(IReplyChannel) ||
				typeof(TChannel) == typeof(IReplySessionChannel);
		}
 
		/// <summary>
		/// Builds the channel factory.
		/// </summary>
		/// <typeparam name="TChannel">The type of the channel.</typeparam>
		/// <param name="context">The context.</param>
		/// <returns></returns>
		public override IChannelFactory<TChannel> BuildChannelFactory<TChannel>(BindingContext context)
		{
			if (context == null)
			{
				throw new ArgumentNullException("context");
			}
 
			if (!CanBuildChannelFactory<TChannel>(context))
			{
				throw new InvalidOperationException(string.Format("ChannelTypeNotSupported - {0}", typeof(TChannel).Name));
			}
 
			if (ManualAddressing)
			{
				throw new InvalidOperationException("ManualAddressingNotSupported");
			}
 
			return new NullChannelFactory<TChannel>(this, context);
		}
 
		/// <summary>
		/// Builds the channel listener.
		/// </summary>
		/// <typeparam name="TChannel">The type of the channel.</typeparam>
		/// <param name="context">The context.</param>
		/// <returns></returns>
		public override IChannelListener<TChannel> BuildChannelListener<TChannel>(BindingContext context)
		{
			if (context == null)
			{
				throw new ArgumentNullException("context");
			}
 
			if (!CanBuildChannelListener<TChannel>(context))
			{
				throw new InvalidOperationException(string.Format("ChannelTypeNotSupported - {0}", typeof(TChannel).Name));
			}
 
			if (typeof(TChannel) == typeof(IInputChannel))
			{
				return (IChannelListener<TChannel>)(new NullInputChannelListener(this, context));
			}
			
			if (typeof(TChannel) == typeof(IInputSessionChannel))
			{
				return (IChannelListener<TChannel>)(new NullInputSessionChannelListener(this, context));
			}
			
			if (typeof(TChannel) == typeof(IReplyChannel))
			{
				return (IChannelListener<TChannel>)(new NullReplyChannelListener(this, context));
			}
 
			return (IChannelListener<TChannel>)(new NullReplySessionChannelListener(this, context));
		}
 
		#endregion
	}
 
	/// <summary>
	/// SupportedSecurityCapabilities
	/// </summary>
	public class SupportedSecurityCapabilities : ISecurityCapabilities
	{
		/// <summary>
		/// Initializes a new instance of the <see cref="SupportedSecurityCapabilities"/> class.
		/// </summary>
		/// <param name="supportedRequestProtectionLevel">The supported request protection level.</param>
		/// <param name="supportedResponseProtectionLevel">The supported response protection level.</param>
		/// <param name="supportsClientAuthentication">if set to <c>true</c> [supports client authentication].</param>
		/// <param name="supportsClientWindowsIdentity">if set to <c>true</c> [supports client windows identity].</param>
		/// <param name="supportsServerAuthentication">if set to <c>true</c> [supports server authentication].</param>
		public SupportedSecurityCapabilities(ProtectionLevel supportedRequestProtectionLevel, ProtectionLevel supportedResponseProtectionLevel,
			bool supportsClientAuthentication, bool supportsClientWindowsIdentity, bool supportsServerAuthentication)
		{
			_supportedRequestProtectionLevel = supportedRequestProtectionLevel;
			_supportedResponseProtectionLevel = supportedResponseProtectionLevel;
			_supportsClientAuthentication = supportsClientAuthentication;
			_supportsClientWindowsIdentity = supportsClientWindowsIdentity;
			_supportsServerAuthentication = supportsServerAuthentication;
		}
 
		#region ISecurityCapabilities Members
 
		private readonly ProtectionLevel _supportedRequestProtectionLevel;
		/// <summary>
		/// Gets the protection level requests supported by the binding.
		/// </summary>
		/// <value></value>
		/// <returns>The <see cref="T:System.Net.Security.ProtectionLevel"/> that specifies the protection level requests supported by the binding.</returns>
		public ProtectionLevel SupportedRequestProtectionLevel
		{
			get { return _supportedRequestProtectionLevel; }
		}
 
		private readonly ProtectionLevel _supportedResponseProtectionLevel;
		/// <summary>
		/// Gets the protection level responses supported by the binding.
		/// </summary>
		/// <value></value>
		/// <returns>The <see cref="T:System.Net.Security.ProtectionLevel"/> that specifies the protection level responses supported by the binding.</returns>
		public ProtectionLevel SupportedResponseProtectionLevel
		{
			get { return _supportedResponseProtectionLevel; }
		}
 
		private readonly bool _supportsClientAuthentication;
		/// <summary>
		/// Gets a value that indicates whether the binding supports client authentication.
		/// </summary>
		/// <value></value>
		/// <returns>true if the binding can support client authentication; otherwise, false.</returns>
		public bool SupportsClientAuthentication
		{
			get { return _supportsClientAuthentication; }
		}
 
		private readonly bool _supportsClientWindowsIdentity;
		/// <summary>
		/// Gets a value that indicates whether the binding supports client Windows identity.
		/// </summary>
		/// <value></value>
		/// <returns>true if the binding can support client Windows identity; otherwise, false.</returns>
		public bool SupportsClientWindowsIdentity
		{
			get { return _supportsClientWindowsIdentity; }
		}
 
		private readonly bool _supportsServerAuthentication;
		/// <summary>
		/// Gets a value that indicates whether the binding supports server authentication.
		/// </summary>
		/// <value></value>
		/// <returns>true if the binding can support server authentication; otherwise, false.</returns>
		public bool SupportsServerAuthentication
		{
			get { return _supportsServerAuthentication; }
		}
 
		#endregion
	}
}
 
 
NullTransportElement.cs
 
 
//*****************************************************************************
//    Description.....Null Transport 
//                                
//    Author..........Roman Kiss, rkiss@pathcom.com
//    Copyright © 2007 ATZ Consulting Inc.  (see included license.rtf file)     
//                        
//    Date Created:    07/07/07
//
//    Date        Modified By     Description
//-----------------------------------------------------------------------------
//    07/07/07    Roman Kiss     Initial Revision
//*****************************************************************************

using System;
using System.ComponentModel;
using System.Configuration;
using System.Net.Security;
using System.ServiceModel.Channels;
using System.ServiceModel.Configuration;
 
namespace Framework.CommunicationFoundation.Bindings
{
	/// <summary>
	/// Configuration section for Null. 
	/// </summary>
	public class NullTransportElement : BindingElementExtensionElement
	{
		private ConfigurationPropertyCollection _properties;
 
		public NullTransportElement()
		{
		}
 
		/// <summary>
		/// Gets the <see cref="T:System.Type"/> object that represents the custom binding element.
		/// </summary>
		/// <value></value>
		/// <returns>A <see cref="T:System.Type"/> object that represents the custom binding type.</returns>
		public override Type BindingElementType
		{
			get { return typeof(NullTransportBindingElement); }
		}
 
		/// <summary>
		/// Returns a custom binding element object.
		/// </summary>
		/// <returns>
		/// A custom <see cref="T:System.ServiceModel.Channels.BindingElement"/> object.
		/// </returns>
		protected override BindingElement CreateBindingElement()
		{
			NullTransportBindingElement bindingElement = new NullTransportBindingElement();
			
			ApplyConfiguration(bindingElement);
 
			return bindingElement;
		}
 
		#region Configuration_Infrastructure_overrides
 
		/// <summary>
		/// Applies the content of a specified binding element to this binding configuration element.
		/// </summary>
		/// <param name="bindingElement">A binding element.</param>
		/// <exception cref="T:System.ArgumentNullException">
		/// 	<paramref name="bindingElement"/> is null.</exception>
		public override void ApplyConfiguration(BindingElement bindingElement)
		{
			base.ApplyConfiguration(bindingElement);
			NullTransportBindingElement element = (NullTransportBindingElement)bindingElement;
			element.SupportedRequestProtectionLevel = SupportedRequestProtectionLevel;
			element.SupportedResponseProtectionLevel = SupportedResponseProtectionLevel;
			element.SupportsClientAuthentication = SupportsClientAuthentication;
			element.SupportsClientWindowsIdentity = SupportsClientWindowsIdentity;
			element.SupportsServerAuthentication = SupportsServerAuthentication;
		}
 
		/// <summary>
		/// Copies the content of the specified configuration element to this configuration element.
		/// </summary>
		/// <param name="from">The configuration element to be copied.</param>
		/// <exception cref="T:System.ArgumentNullException">
		/// 	<paramref name="from"/> is null.</exception>
		/// <exception cref="T:System.Configuration.ConfigurationErrorsException">The configuration file is read-only.</exception>
		public override void CopyFrom(ServiceModelExtensionElement from)
		{
			base.CopyFrom(from);
			NullTransportElement element = (NullTransportElement)from;
			SupportedRequestProtectionLevel = element.SupportedRequestProtectionLevel;
			SupportedResponseProtectionLevel = element.SupportedResponseProtectionLevel;
			SupportsClientAuthentication = element.SupportsClientAuthentication;
			SupportsClientWindowsIdentity = element.SupportsClientWindowsIdentity;
			SupportsServerAuthentication = element.SupportsServerAuthentication;
		}
 
		/// <summary>
		/// Initializes this binding configuration section with the content of the specified binding element.
		/// </summary>
		/// <param name="bindingElement">A binding element.</param>
		protected override void InitializeFrom(BindingElement bindingElement)
		{
			base.InitializeFrom(bindingElement);
			NullTransportBindingElement element = (NullTransportBindingElement)bindingElement;
			SupportedRequestProtectionLevel = element.SupportedRequestProtectionLevel;
			SupportedResponseProtectionLevel = element.SupportedResponseProtectionLevel;
			SupportsClientAuthentication = element.SupportsClientAuthentication;
			SupportsClientWindowsIdentity = element.SupportsClientWindowsIdentity;
			SupportsServerAuthentication = element.SupportsServerAuthentication;
		}
 
		/// <summary>
		/// Gets the collection of properties.
		/// </summary>
		/// <value></value>
		/// <returns>
		/// The <see cref="T:System.Configuration.ConfigurationPropertyCollection"/> of properties for the element.
		/// </returns>
		protected override ConfigurationPropertyCollection Properties
		{
			get
			{
				if (_properties == null)
				{
					_properties = new ConfigurationPropertyCollection();
					_properties.Add(new ConfigurationProperty("supportedRequestProtectionLevel", typeof(ProtectionLevel), ProtectionLevel.None, null, null, ConfigurationPropertyOptions.None));
					_properties.Add(new ConfigurationProperty("supportedResponseProtectionLevel", typeof(ProtectionLevel), ProtectionLevel.None, null, null, ConfigurationPropertyOptions.None));
					_properties.Add(new ConfigurationProperty("supportsClientAuthentication", typeof(bool), false, null, null, ConfigurationPropertyOptions.None));
					_properties.Add(new ConfigurationProperty("supportsClientWindowsIdentity", typeof(bool), false, null, null, ConfigurationPropertyOptions.None));
					_properties.Add(new ConfigurationProperty("supportsServerAuthentication", typeof(bool), false, null, null, ConfigurationPropertyOptions.None));
				}
 
				return _properties;
			}
		}
 
		#endregion
 
		/// <summary>
		/// Gets or sets the supported request protection level.
		/// </summary>
		/// <value>The supported request protection level.</value>
		[TypeConverter(typeof(EnumConverter))]
		[ConfigurationProperty("supportedRequestProtectionLevel", DefaultValue = ProtectionLevel.None)]
		public ProtectionLevel SupportedRequestProtectionLevel
		{
			get
			{
				return (ProtectionLevel)base["supportedRequestProtectionLevel"];
			}
 
			set
			{
				base["supportedRequestProtectionLevel"] = value;
			}
		}
 
		/// <summary>
		/// Gets or sets the supported response protection level.
		/// </summary>
		/// <value>The supported response protection level.</value>
		[TypeConverter(typeof(EnumConverter))]
		[ConfigurationProperty("supportedResponseProtectionLevel", DefaultValue = ProtectionLevel.None)]
		public ProtectionLevel SupportedResponseProtectionLevel
		{
			get
			{
				return (ProtectionLevel)base["supportedResponseProtectionLevel"];
			}
 
			set
			{
				base["supportedResponseProtectionLevel"] = value;
			}
		}
 
		/// <summary>
		/// Gets or sets a value indicating whether [supports client authentication].
		/// </summary>
		/// <value>
		/// 	<c>true</c> if [supports client authentication]; otherwise, <c>false</c>.
		/// </value>
		[ConfigurationProperty("supportsClientAuthentication", DefaultValue = false)]
		public bool SupportsClientAuthentication
		{
			get
			{
				return (bool)base["supportsClientAuthentication"];
			}
 
			set
			{
				base["supportsClientAuthentication"] = value;
			}
		}
 
		/// <summary>
		/// Gets or sets a value indicating whether [supports client windows identity].
		/// </summary>
		/// <value>
		/// 	<c>true</c> if [supports client windows identity]; otherwise, <c>false</c>.
		/// </value>
		[ConfigurationProperty("supportsClientWindowsIdentity", DefaultValue = false)]
		public bool SupportsClientWindowsIdentity
		{
			get
			{
				return (bool)base["supportsClientWindowsIdentity"];
			}
 
			set
			{
				base["supportsClientWindowsIdentity"] = value;
			}
		}
 
		/// <summary>
		/// Gets or sets a value indicating whether [supports server authentication].
		/// </summary>
		/// <value>
		/// 	<c>true</c> if [supports server authentication]; otherwise, <c>false</c>.
		/// </value>
		[ConfigurationProperty("supportsServerAuthentication", DefaultValue = false)]
		public bool SupportsServerAuthentication
		{
			get
			{
				return (bool)base["supportsServerAuthentication"];
			}
 
			set
			{
				base["supportsServerAuthentication"] = value;
			}
		}
	}
}

GeneralRe: security capabilities problem PinmemberGoffkock7-Dec-09 20:29 
GeneralRe: security capabilities problem PinmemberDeyan Petrov7-Dec-09 21:10 
GeneralHello Pinmembersamadbukhari17-Mar-09 21:11 
GeneralPerformance (latency) of nullTransport Pinmemberbram0528-Jan-08 0:18 
GeneralRe: Performance (latency) of nullTransport PinmemberRoman Kiss28-Jan-08 17:43 
GeneralRe: Performance (latency) of nullTransport PinmemberRoman Kiss4-Feb-08 18:26 
GeneralRe: Performance (latency) of nullTransport Pinmemberalhambra-eidos9-Dec-10 0:17 
GeneralA Question about another type of transport PinmemberOsama Askari17-Oct-07 4:07 
GeneralNeed Executive Summary PinmemberCommonGenius9-Oct-07 6:08 
GeneralPerformance improvement specifics PinmemberDale Thompson9-Oct-07 4:28 
GeneralRe: Performance improvement specifics PinmemberRich Crane4-Nov-07 18:45 
GeneralVery worthwhile effort! Pinmemberrusmo279-Oct-07 4:06 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140821.2 | Last Updated 1 Oct 2007
Article Copyright 2007 by Roman Kiss
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid