Using Protocol Buffers on .Net platform (Part II)

The last time we familiarized ourselves with Protocol Buffers and tried to use them in .Net application. Today, we continue to discuss and answer remaining questions. Specifically let’s talk again about portability and using of protobuf-net in WCF.

Wait a minute, can that we serialized on one platform be deserialized on the other?

Yes and no. Protobuf describes simple data types for whose the answer is yes, but, unfortunately, there are data types for whose the answer is no.  And our example proves it! Pay attention to DateTime type. It is itself a complex data type and also platform specific. Therefore, if we serialize it, then there is no guarantee that it will be restored on another platform. We should find a way to correct this drawback. One solution is already implemented in protobuf-net and is as follows: DateTime type is serialized as a time interval that has elapsed since 01/01/1970 (the unix-time origin).  Agreements for other types here.

In order to protect themselves from such situations, it is recommended to use files with .proto extension to describe your data structures (integration with Visual Studio is supported).  A generator will help to generate the source code to take into account peculiarities of programming language and a portable representation of your structured data. In our case we have the following description (in the example corresponds to its own DateTime type on the basis of this discussion):

package Proto.Common;
message Task
{
    optional int32 Id = 1;
    optional DateTime CreatedAt = 2;
    optional string CreatedBy = 3;
    enum TaskPriority
    {
        Low = 0;
        Medium = 1;
        High = 2;
    }
    optional TaskPriority Priority = 4;
    optional string Content = 5;
}

As generated code is too complex and is not intended for manual editing, we omit it.

I noticed Javascript in the list of supported languages​​, whether to abandon JSON in favor of protobuf?

Definitely not. JSON has a built-in support for each browser, and you do not need to unload a script for its serialization and deserialization on the client. If you are experiencing problems with the volume of data you should consider, for example, to enable compression. However, this implementation you will definitely come in handy if you develop using server-side Javascript, for example, in NodeJS.

Where else can I use protobuf than the example described above?

As .Net developer you will find in protobuf-net support for such platforms as Silverlight, Compact Framework, Mono, and others. Let’s see how to easily integrate protobuf in WCF. Traditionally we will begin with a description of our entity (for TaskPriority nothing has changed):

[DataContract]
public class Task
{
	[DataMember(Order = 1)]
	public int Id { get; set; }

	[DataMember(Order = 2)]
	public DateTime CreatedAt { get; set; }

	[DataMember(Order = 3)]
	public string CreatedBy { get; set; }

	[DataMember(Order = 4)]
	public TaskPriority Priority { get; set; }

	[DataMember(Order = 5)]
	public string Content { get; set; }
}

As you can see, it differs little from the previously given description. But honestly, it does not look like as an example of using protobuf! The service contract:

[ServiceContract]
public interface ITaskManager
{
	[OperationContract]
	Task[] GetTasks();
}

Again, no hint of protobuf. The only thing where you can mention it is the configuration file of the service:

<?xml version="1.0"?>
<configuration>
    <system.serviceModel>
        <services>
            <service name="Proto.Server.TaskManager"
                     behaviorConfiguration="Proto.Server.ServiceBehavior">
                <!-- Service Endpoints -->
                <endpoint name="Proto.Server.Endpoint"
                         address="net.tcp:\\localhost:9000"
                         binding="netTcpBinding"
                         contract="Proto.Common.ITaskManager"
                         behaviorConfiguration="Proto.Common.EndpointBehavior" />
            </service>
        </services>
        <behaviors>
            <serviceBehaviors>
                <behavior name="Proto.Server.ServiceBehavior">
                    <!-- To receive exception details in faults for debugging purposes, set the value below to true.
                         Set to false before deployment to avoid disclosing exception information -->
                    <serviceDebug includeExceptionDetailInFaults="true"/>
                </behavior>
            </serviceBehaviors>
            <endpointBehaviors>
                <behavior name="Proto.Common.EndpointBehavior">
                    <protobuf/>
                </behavior>
            </endpointBehaviors>
        </behaviors>
        <extensions>
            <behaviorExtensions>
                <add name="protobuf"
                     type="ProtoBuf.ServiceModel.ProtoBehaviorExtension, protobuf-net, Version=1.0.0.280, Culture=neutral, PublicKeyToken=257b51d87d2e4d67"/>
            </behaviorExtensions>
        </extensions>
    </system.serviceModel>
</configuration>

And for the client:

<?xml version="1.0"?>
<configuration>
    <system.serviceModel>
        <client>
            <endpoint name="Proto.Client.Endpoint"
                     address="net.tcp:\\localhost:9000"
                     binding="netTcpBinding"
                     contract="Proto.Common.ITaskManager"
                     behaviorConfiguration="Proto.Common.EndpointBehavior" />
        </client>
        <behaviors>
            <endpointBehaviors>
                <behavior name="Proto.Common.EndpointBehavior">
                    <protobuf/>
                </behavior>
            </endpointBehaviors>
        </behaviors>
        <extensions>
            <behaviorExtensions>
                <add name="protobuf"
                     type="ProtoBuf.ServiceModel.ProtoBehaviorExtension, protobuf-net, Version=1.0.0.280, Culture=neutral, PublicKeyToken=257b51d87d2e4d67"/>
            </behaviorExtensions>
        </extensions>
    </system.serviceModel>
</configuration>

The implementation of the client and the service is trivial and uninteresting.

If you enable logging of messages, you can see the following entry in the log:

<MessageLogTraceRecord Time="2011-05-12T16:17:03.2780000+04:00" Source="TransportSend" Type="System.ServiceModel.Dispatcher.OperationFormatter+OperationFormatterMessage" xmlns="http://schemas.microsoft.com/2004/06/ServiceModel/Management/MessageTrace">
    <s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing">
        <s:Header>
            <a:Action s:mustUnderstand="1">
                tempuri.org/ITaskManager/GetTasksResponse
            </a:Action>
            <a:RelatesTo>
                urn:uuid:768e2f40-612c-4593-8cfb-eacb7b291a9c
            </a:RelatesTo>
            <a:To s:mustUnderstand="1">
                www.w3.org/2005/08/addressing/anonymous
            </a:To>
        </s:Header>
        <s:Body>
            <GetTasksResponse xmlns="http://tempuri.org/">
                <proto>
                    Ci4IARIJCPTs+c/8SxAEGgpTdGV2ZSBKb2JzIAIqEUludmVudCBuZXcgaVBob25lCi8IAhIJCPTclY/4SxAEGg1TdGV2ZSBCYWxsbWVyKhFJbnN0YWxsIG93biBTa3lwZQ==
                </proto>
            </GetTasksResponse>
        </s:Body>
    </s:Envelope>
</MessageLogTraceRecord>

As I remember using of data contracts is not limited to the attributes. How are things going with the extension of these contracts?

Exactly as in WCF.  You know that you can take into account changes in data contracts with IExtensibleDataObject interface. In protobuf-net it is similar and the interface is called IExtensible. The difference is not so much so that you can explore it yourself here.

Why do so many developers refuse to use protobuf-net in WCF?

This is nothing more than a myth.  Just developers usually find protobuf, when looking for solutions to accelerate their network communications. Some of them go further and remove levels of abstraction, imposed by WCF, and used, for example, TcpClient for maximum speed. If this is the case, then perhaps you will find more useful Thrift.

You’ve convinced me. I am sure to try protobuf-net in the next project. Thanks for the discussion!

Please. The project is actively developed, so that perhaps we will discuss it again and again.

Posted on by osmirnov Posted in .NET, WCF

Add a Comment