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.

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:

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:

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:

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:

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:

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.

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.

We have daemon process hosting WCF services and providing them to MS Office add-in processes through named pipes. Very often WCF services communicate inside process with each other. I thought avoiding named pipes and using shared managed memory in process will give some performance boosts to this communication still using WCF semantics regarding session and multithreading.
But my tests show that this improvement is very small or even sometimes slowdown happens if to compare to named pipes, especially when using with SessionMode = SessionMode.Required.

I've tried to compile code from NullTransportForWCF_src35.zip and got the following error:
F:\sources\NullChannel\Test\WorkflowLibrary1\CreateWorkflowAttribute.cs(33,21) : error CS1061: 'System.ServiceModel.Channels.ContextMessageProperty' does not contain a definition for 'TryGetValue' and no extension method 'TryGetValue' accepting a first argument of type 'System.ServiceModel.Channels.ContextMessageProperty' could be found (are you missing a using directive or an assembly reference?)

maybe offtopic but,.. I compared perfomance of TCP and NamedPipe transports. TCP uses more OS resources (cpu time), NamesPipe uses more resource on application side. So actually there are no big difference what to use.

First of all - thanks for the null transport - it is a great piece of code!

If I try to use the null transport in a custom binding with

<security authenticationMode="UserNameOverTransport"></security>

then I get the following exception

"The security capabilities of binding 'System.ServiceModel.Channels.CustomBinding' do not match those of the generated runtime object. Most likely this means the binding contains a StreamSecurityBindingElement, but lacks a TransportBindingElement that supports Stream Security (such as TCP or Named Pipes). Either remove the unused StreamSecurityBindingElement or use a transport that supports this element."

I tried to modify the NullTransportBindingElement by changing the GetProperty method like this

but then your sample don't work, with the same Exception as above. It seems that either a transport is security aware - then there should be also security element configured in the binding, or it is not security aware, in which case there should be NO security element in the binding. I need an intelligent transport which supports both, however - is that possible?

Yes - in order to use WS-Security or UsernameToken for example the underlying channel should be secure. WCF checks if the nulltransport is secure, which is not the case, and throws an exception. Using the configuration I pasted above you can make the nullchannel secure.

I have done some latency measurements on WCF, and included the nullTransport in it. See here http://geekswithblogs.net/BVeldhoen/archive/2008/01/26/wcf-latency-test-harness.aspx for some results.

I could not reproduce that nullTransport is 'faster' than netNamedPipe, regardless the message size and serializers. Could you please take a look and tell me where I went wrong? A link to the code is provided at the bottom of the blog.

First of all, thank you for your great performance latency test program which included also the nullTransport channel. Unfortunately, I didn’t have an enough time to go in details, but based on my first look the nullTransport is configured correctly.

Your article was great as usual. I think i'm going to test it and use it in production (of course if you give me the permission).

I have a problem in implementing a Chunking channel based on HTTP protocol. I need this kind of transport to overcome the problem of sending and receiving large files. I have TCP implementation of this idea that is for microsoft. I tried to change it to use HTTP but i was totally unsuccessful. May you please guide me about this matter?

It looks like you have an interesting article here, but it is reasonably long and clearly very in-depth. Before I spend a lot of time trying to understand what you are trying to present, I would like to be able to determine quickly if the content is something I would be interested in. It would be very useful if the first paragraph of your article summarized what a NULL transport is and why it would be useful, so I can decide if it is something I might want to learn about.

AGREED! Great concept! The WCF team has yet to provide a true in-process transport. I am sure they are working on it though. I have been eager to create an in-process transport for some time now. I am glad someone beat me too it.

I tested the performance of the NullTransport and it was slower (and less scalable) than the NetNamedPipeTransport. I am sure it is just a matter of a performance and scalability tuning exercise to get this on-par or better.