WCF Routing

As a little experiment I’ve been playing with a WCF router. Basically a router will accept any request on a certain binding and forward the request to the real service. There are quite some examples available on the internet but they were never complete or everything was done in code instead of using the configuration file.

The service:
This could be any service, no special code is required.

The client:
Any client, no special code required.
I only created a behavior extension and message inspector for adding some headers with information (binding, contract, isOneWay) for the router before sending the request. These are “attached” to the endpoint by configuration so there is no impact on the implementation of the client itself. The binding and contract headers will be used to determine where the request has to be routed to. The isOneWay header is needed to distinguish request-reply from one-way messages, this will be explained in the next component.

The router service:
This is the part that does all the work. The routing is based on the binding and contract and my implementation is capable of handling standard request-reply and one way messages. If you don’t distinguish between them the router will throw timeout exceptions.

To accomplish this obstacle you first need three service contracts for the router.

[ServiceContract]

public interface IRouter

{

[OperationContract(ReplyAction = "*")]

Message Action(Message msg);

[OperationContract(IsOneWay = true)]

void OneWayAction(Message msg);

}

[ServiceContract]

public interface IGenericContract

{

[OperationContract(Action = "*", ReplyAction = "*")]

Message Action(Message message);

}

[ServiceContract]

public interface IOneWayGenericContract

{

[OperationContract(Action = "*", IsOneWay = true)]

void Action(Message message);

}

IRouter is the real service contract of the router service, IGenericContract and IOneWayGenericContract are only used as contracts in the endpoint configurations to the real service, they do not have an implementation. Also pay attention to the Action=”*” attribute, without this the WCF infrastructure will complain about incompatible actions. This is also the reason why we need to make two separate contracts, you can only have one OperationContract with Action = “*”.

The second part of the solution to the “standard” vs “one way” request problem is a DispatchOperationSelector, implemented as an endpoint behavior. This little critter will return the name of the operation that must be executed on the router. To make its decision it will check the header “isOneWay” that was added by message inspector on the client side. If the value of the header is true it will return “OneWayAction”, corresponding to the IRouter.OneWayAction method, else it will return “Action”, corresponding to the IRouter.Action method.

sealed class DispatchOperationSelector : IDispatchOperationSelector, IEndpointBehavior

{

#region IEndpointBehavior Members

public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)

{

}

public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)

{

}

public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)

{

endpointDispatcher.ContractFilter = new MatchAllMessageFilter();

endpointDispatcher.DispatchRuntime.OperationSelector = this;

}

public void Validate(ServiceEndpoint endpoint)

{

}

#endregion

#region IDispatchOperationSelector Members

public string SelectOperation(ref Message message)

{

if (message.Headers.GetHeader<bool>(“isOneWay”, “http://company.com”))

{

return “OneWayAction”;

}

else

{

return “Action”;

}

}

#endregion

}

In the configuration file of the router there are 3 important parts
The mapping definition:

<routerServiceMappingxmlns=http://company.com>

<bindings>

<bindingname=nettcpbinding>

<contracts>

<contractname=ServiceContracts.IConcatendpoint=ConcatService/>

<contractname=ServiceContracts.ICalcendpoint=CalcService/>

</contracts>

</binding>

<bindingname=basichttpbinding>

<contracts>

<contractname=ServiceContracts.ICalcendpoint=HttpCalcService/>

</contracts>

</binding>

</bindings>

</routerServiceMapping>

For you information, to create custom configuration sections I use the Configuration Section Designer plugin of Jelle Druyts, this is really a must have.

The service definition:

<services>

<servicename=Router.RouterServicebehaviorConfiguration=“” >

<endpoint name=RouterService

address=net.tcp://localhost:3000/Router

contract=Router.IRouter

binding=netTcpBinding

behaviorConfiguration=DispatchBehavior/>

<endpoint name=RouterService

address=http://localhost:3001/Router

contract=Router.IRouter

binding=basicHttpBinding

behaviorConfiguration=DispatchBehavior/>

</service>

</services>

You’ll need one for each binding you want to support.

The client endpoints:

<client>

<endpoint name=ConcatService

address=net.tcp://localhost:2000/Concat

contract=Router.IGenericContract

binding=netTcpBinding/>

<endpoint name=ConcatService

address=net.tcp://localhost:2000/Concat

contract=Router.IOneWayGenericContract

binding=netTcpBinding/>

<endpoint name=HttpCalcService

address=http://localhost:2001/Calc

contract=Router.IGenericContract

binding=basicHttpBinding/>

<endpoint name=HttpCalcService

address=http://localhost:2001/Calc

contract=Router.IOneWayGenericContract

binding=basicHttpBinding/>

<endpoint name=CalcService

address=net.tcp://localhost:2002/Calc

contract=Router.IGenericContract

binding=netTcpBinding/>

<endpoint name=CalcService

address=net.tcp://localhost:2002/Calc

contract=Router.IOneWayGenericContract

binding=netTcpBinding/>

</client>

You’ll need one for each binding-contract-oneway combination you want to support.
I know that if you have a lot of services to route this will become a big mess, in that case you’ll have to look for another solution to “register” your services with the router. One possible solution is that the services themselves register with the router. This might seem as if the services need to know that there is a routing infrastructure but it will actually be the host or maybe an extension that will take care of the registering, the service implementation itself does not need to know that it can receive routed messages.

Download the complete solution, just keep in mind that this is “proof of concept” code that is not completely optimized, could contain bugs and is not intended to be used directly in real life applications.

If you have any comments, ideas or improvements on the code please let me know.

Other WCF articles:
- MaxItemsInObjectGraph and keeping references when serializing in WCF
- Tracing WCF messages
- Use your WCF proxies in a safe way

Advertisement
This entry was posted in WCF and tagged , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

Gravatar
WordPress.com Logo

Please log in to WordPress.com to post a comment to your blog.

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s