{ explore .net }

To content | To menu | To search

Tuesday 25 November 2008

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.IConcat"endpoint="ConcatService"/>

                <contractname="ServiceContracts.ICalc"endpoint="CalcService"/>

            </contracts>

        </binding>

        <bindingname="basichttpbinding">

            <contracts>

                <contractname="ServiceContracts.ICalc"endpoint="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.RouterService"behaviorConfiguration="" >

        <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.

The complete solution is attached to the post, 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

Friday 26 September 2008

Use your WCF proxies in a safe way

In a lot of WCF examples you’ll see that the proxy is called in a using statement, this is actually not the best or safest way to call your proxy.

When you write

using (ServiceProxy proxy = new ServiceProxy())

{

    proxy.SomeAction();

}

It will be translated to something like this:

ServiceProxy proxy = new ServiceProxy();

try

{

    proxy.SomeAction();

}

finally

{

    if (proxy != null)

        ((IDisposable)proxy).Dispose();

}

The problem is that proxy.Dispose() will throw an exception when the proxy is in a faulted state and could mask any exceptions thrown in the try block. This is not a desirable effect, in the finally block we just want to cleanup the proxy even if it’s in a faulted state. The solution I propose will let you use your proxy in safe and clean way.

WcfProxy.Using<ServiceProxy>(proxy =>

{

    proxy.SomeAction();

});

or if you want to create the proxy object yourself:

WcfProxy.Using<ServiceProxy>(new ServiceProxy(), proxy =>

{

    proxy.SomeAction();

});

You only need to use generics and the Action<> delegate.

public static void Using<TProxy>(TProxy proxy, Action<TProxy> action)

    where TProxy : ICommunicationObject

{

    try

    {               

        action(proxy);

    }

    catch (Exception)

    {

        proxy.Abort();

        throw;

    }

 

    try

    {              

        proxy.Close();

    }           

    catch (Exception)

    {

        proxy.Abort();

    }

}


In the attached .cs file you will find the complete implementation and an overloaded method that will create the proxy instance for you. The .snippet file will save you even more work.

Update:
If the service returns data you have three ways of getting/using that data. The first way is to do everything in the delegate/lambda you pass to the Using() method.

WcfProxy.Using<ServiceProxy>(proxy =>

{

    List<Contact> contacts = proxy.GetContacts();

    // do more work here

});


The second way is to use "external" variables in the lambda:

List<Contact> contacts = null;

WcfProxy.Using<ServiceProxy>(proxy =>

{

    contacts = proxy.GetContacts();

});

// do more work here


The third way is to create an additional Using() method:

public static TReturn Using<TProxy, TReturn>(TProxy proxy, Func<TProxy, TReturn> function)

    where TProxy : ICommunicationObject

{

    TReturn result = default(TReturn);

 

    try

    {

        result = function(proxy);

    }

    // ...

    return result; 

}



And use it like this:

List<Contact> contacts = WcfProxy.Using<ServiceProxy, List<Contact>>(new ServiceProxy(), proxy =>

{

    return proxy.GetContacts();

});



Other WCF articles:
- MaxItemsInObjectGraph and keeping references when serializing in WCF
- Tracing WCF messages
- WCF Routing

Sunday 26 August 2007

Tracing WCF messages

When you need to see the actual WCF message that gets over the wire you could use tools such as TcpTrace but this won't work immediatly.

The security of WCF doesn't allow communication when the "To" header of the message isn't the same as the endpoint address, to make it work you can do the following:

1. Change the configuration of your service to include a "listenUri" attribute:

<endpoint
   address="http://localhost:888/SomeService"
   listenUri="http://localhost:777/SomeService"
   binding="basicHttpBinding"
   contract="ISomeService"/>

This will cause the actual listener to run on port 777, in the WSDL you'll still see port 888.

2. TcpTrace configuration:

listen port: 888
destination port: 777

3. On the client side make sure the endpoint is configured to send the messages to port 888 when TcpTrace is running.

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

Wednesday 22 August 2007

MaxItemsInObjectGraph and keeping references when serializing in WCF

Yesterday when I was implementing a new WCF service I encountered a problem that the response of the service contained too much items ("System.Runtime.Serialization.SerializationException : Maximum number of items that can be serialized or deserialized in an object graph is '65536'. Change the object graph or increase the MaxItemsInObjectGraph quota."). Items will probably also include types such as string, I only had about 2000 real object that needed to be returned.

After some searching I found a solution on http://blogs.inetium.com/blogs/dmork/archive/2007/02/27/896.aspx. In short what you need to do is to tell the DataContractSerializer that it should allow more items in the object graph but you can only set this value in the constructor of the DataContractSerializer.

A first possibility is that you create service and/or endpoint behaviors in the configuration of your WCF service:

<behaviors>
  <serviceBehaviors>
    <behavior name="LargeServiceBehavior">
      <dataContractSerializer maxItemsInObjectGraph="100000"/>
    </behavior>
  </serviceBehaviors>
</behaviors>


and

<behaviors>
  <endpointBehaviors>
    <behavior name="LargeEndpointBehavior">
      <dataContractSerializer maxItemsInObjectGraph="100000"/>
    </behavior>
  </endpointBehaviors>
</behaviors>


You add these in the <system.serviceModel> element. Don't forget to set the "behaviorConfiguration" attribute in the configuration of your service or endpoint.

For most implementations this will work, of course for me it didn't :-).

After a lot of trying and searching I realized that I already created a custom initialized DataContractSerializer. On the methods (OperationContract) of my service (ServiceContract) I already added a custom attribute to preserve references between objects when serializing. This attribute creates a DataContractSerializer with one of the constructors that allows you to preserve references. Because I created the DataContractSerializer myself the configuration was never taken into account. So in my solution I created new attribute that can take 2 parameters (a bool to preserve references and an integer to set the limit of the object grap).

code for the attribute :
public class CustomDataContractSerializerAttribute : Attribute, IOperationBehavior
{
   private bool _preserveReferences = false;
   private int _maxItemsInObjectGraph = 65536;

   public CustomDataContractSerializerAttribute(bool preserveReferences, int maxItemsInObjectGraph)
   {
      _preserveReferences = preserveReferences;
      _maxItemsInObjectGraph = maxItemsInObjectGraph;
   }

   public void AddBindingParameters(OperationDescription description, BindingParameterCollection parameters)
   {}

   public void ApplyClientBehavior(OperationDescription description, ClientOperation proxy)
   {
      IOperationBehavior innerBehavior = new CustomDataContractSerializerOperationBehavior(description, _preserveReferences, _maxItemsInObjectGraph);
      innerBehavior.ApplyClientBehavior(description, proxy);
   }
  
   public void ApplyDispatchBehavior(OperationDescription description, DispatchOperation dispatch)
   {
      IOperationBehavior innerBehavior = new CustomDataContractSerializerOperationBehavior(description, _preserveReferences, _maxItemsInObjectGraph);
      innerBehavior.ApplyDispatchBehavior(description, dispatch);
   }
  
   public void Validate(OperationDescription description)
   { }
}


code for the operation behavior :
public class CustomDataContractSerializerOperationBehavior : DataContractSerializerOperationBehavior
{
   private bool _preserveReferences = false;
   private int _maxItemsInObjectGraph = 65536;

   public CustomDataContractSerializerOperationBehavior(OperationDescription operationDescription, bool preserveReferences, int maxItemsInObjectGraph)
      : base(operationDescription)
   {
      _preserveReferences = preserveReferences;
      _maxItemsInObjectGraph = maxItemsInObjectGraph;
   }
  
   public override XmlObjectSerializer CreateSerializer(Type type, string name, string ns, IList knownTypes)
   {
      return new DataContractSerializer(type, name, ns, knownTypes, this.MaxItemsInObjectGraph, this.IgnoreExtensionDataObject, _preserveReferences, this.DataContractSurrogate);
   }

   public override XmlObjectSerializer CreateSerializer(Type type, XmlDictionaryString name, XmlDictionaryString ns, IList knownTypes)
   {
      return new DataContractSerializer(type, name, ns, knownTypes, _maxItemsInObjectGraph, this.IgnoreExtensionDataObject, _preserveReferences, this.DataContractSurrogate);
   }
}


and now I write my service contract like this :
[ServiceContract(Namespace = "urn:SomeNamespace/IServiceIntervace")]
public interface IServiceInterface
{
   [OperationContract(Name = "MyOperation", Action = "urn:SomeNamespace/IServiceIntervace/MyOperation")]
   [CustomDataContractSerializer(true, 100000)] /* preserve references and maximum 100000 items in the object graph */  
   MyReturnType MyOperation();
}


The original code of the attribute to preserve references comes from http://blogs.msdn.com/drnick/archive/2007/05/15/replacing-the-serializer-part-1.aspx, this article also explains some problems with previous versions of the code examples to create a custom initialized DataContractSerializer.

If you have problems with you services, don't forget to enable tracing.

Other WCF articles:
- Tracing WCF messages
- WCF Routing
- Use your WCF proxies in a safe way