2

I have a WCF Contract outlining a test method that just returns an instance of a class across WCF using protobuf-net. I can serialize and deserialize in a test application but when I make the request via WCF the response the class instance exists, but all its properties are null.

Here are the relevant config files and class definitions:

[ProtoContract]
public class TestClass
{
    [ProtoMember(1)]
    public int TestInt { get; set; }

    [ProtoMember(2)]
    public string TestString { get; set; }
}

...

[ServiceContract]
public interface ITestService
{
    [OperationContract]
    [ProtoBehavior]
    TestClass RunTest(int x);
}

...

<extensions>
    <behaviorExtensions>
        <add name="protobuf" type="ProtoBuf.ServiceModel.ProtoBehaviorExtension, protobuf-net, Version=1.0.0.282, Culture=neutral, PublicKeyToken=257b51d87d2e4d67" />
    </behaviorExtensions>
</extensions>

<endpointBehaviors>
    <behavior name="Proto.Default.EndpointBehavior">
        <protobuf />
    </behavior>
</endpointBehaviors>
<serviceBehaviors>
    <behavior name="Proto.Default.ServiceBehavior">
      <serviceDebug includeExceptionDetailInFaults="true" />
      <serviceMetadata />
      <serviceAuthorization principalPermissionMode="None" />
      <serviceThrottling    maxConcurrentCalls="250"
                            maxConcurrentSessions="200"
                            maxConcurrentInstances="10" />
    </behavior>
</serviceBehaviors>

...

<services>
    <service name="WcfTestService" behaviorConfiguration="Proto.Default.ServiceBehavior">
    <endpoint address=""    binding="netTcpBinding" bindingConfiguration="NetTcpBinding_ITestService"   contract="ITestService" behaviorConfiguration="Proto.Default.EndpointBehavior" />
    <endpoint address="mex" binding="customBinding" bindingConfiguration="myMexTcpBinding" contract="IMetadataExchange" />
    <host>
        <baseAddresses>
            <add baseAddress="net.tcp://localhost:2517/TestService" />
        </baseAddresses>
    </host>
</service>

...

<client>
   <endpoint address="net.tcp://localhost:2517/TestService"
    binding="netTcpBinding" bindingConfiguration="NetTcpBinding_ITestService"
    contract="ITestService" name="TestService"
    behaviorConfiguration="Proto.Default.EndpointBehavior">
    <identity>
        <dns value="localhost" />
    </identity>
</endpoint>
</client>

I can debug the service and see the request come across. The TestClass object is created and returned. I stepped through protobuf-net source code and the deserialize method runs and it just creates a blank instance of TestClass and iterates through the data that is returned but never sets any of the properties.

Oh, I used Mex to generate a proxy.

EDIT

Here is the MEX generated class for TestClass

[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "4.0.0.0")]
[System.Runtime.Serialization.DataContractAttribute(Name="TestClass", Namespace="http://schemas.datacontract.org/2004/07/TestProject")]
[System.SerializableAttribute()]
public partial class TestClass : object, System.Runtime.Serialization.IExtensibleDataObject, System.ComponentModel.INotifyPropertyChanged {

    [System.NonSerializedAttribute()]
    private System.Runtime.Serialization.ExtensionDataObject extensionDataField;

    [System.Runtime.Serialization.OptionalFieldAttribute()]
    private int TestIntField;

    [System.Runtime.Serialization.OptionalFieldAttribute()]
    private string TestStringField;

    [global::System.ComponentModel.BrowsableAttribute(false)]
    public System.Runtime.Serialization.ExtensionDataObject ExtensionData {
        get {
            return this.extensionDataField;
        }
        set {
            this.extensionDataField = value;
        }
    }

    [System.Runtime.Serialization.DataMemberAttribute()]
    public int TestInt {
        get {
            return this.TestIntField;
        }
        set {
            if ((this.TestIntField.Equals(value) != true)) {
                this.TestIntField = value;
                this.RaisePropertyChanged("TestInt");
            }
        }
    }

    [System.Runtime.Serialization.DataMemberAttribute()]
    public string TestString {
        get {
            return this.TestStringField;
        }
        set {
            if ((object.ReferenceEquals(this.TestStringField, value) != true)) {
                this.TestStringField = value;
                this.RaisePropertyChanged("TestString");
            }
        }
    }

    public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;

    protected void RaisePropertyChanged(string propertyName) {
        System.ComponentModel.PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
        if ((propertyChanged != null)) {
            propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
        }
    }
}
2
  • Can you show the Mex-generated proxy? There are ways of coercing the proxy to play nicely, but I'd need to see it. In particular, I'm after the members marked DataMember, along with their attributes Commented May 25, 2011 at 23:04
  • I posted the code generated for TestClass above. Commented May 26, 2011 at 15:12

2 Answers 2

1

Right; Mex/generation hasn't included the numbers, but you can add them. In a separate code file (same namespace), add

[ProtoContract]
[ProtoPartialMember(1, "TestInt")]
[ProtoPartialMember(2, "TestString")]
partial class TestClass {}

Sometimes WCF helps, sometimes it is a pain (note it works easy if you have a shared DTO assembly at both ends).

Sometimes WCF gives the right-ish numbers on each DataMember, but off-by-1 - if that happens there's a tweak you can use to just tell protobuf to use those numbers with an offset.

Sign up to request clarification or add additional context in comments.

5 Comments

Welllll, can't really blame WCF here. The use of custom serialization attributes (i.e. ProtoMember vs. DataMember) means that the proto buffer behvior implementation would be responsible for generating proper schema details to the WSDL by implementing IWsdlExportExtension. Since it's not, it will default to alphabetical order for the members.
@Drew and for this dual-purpose, protobuf-net is perfectly happy working from DataContract/DataMember attributes :)
@Drew - however, that interface is a new one to me; I will take a look
@Marc Gravell You could also probably use DataMemberAttribute AND ProtoMemberAttribute together where DMA helps gives you the ordering and PMA helps with all the protobuf specific support. Then you wouldn't need to do anything specific with IWsdlExport/ImportExtension. The downside is a little more coding and duplication of the order concept between DMA's Order and PMA's Tag. I bet you can get it to work with IWsdlExportExtension though, def. check it out.
@Andy share assembles are a breeze for this - by far the easiest option for what is already a pretty custom scenario
0

I think you're hitting famous interoperability issue here. Please see my answer to this post: At client side, return value of WCF Operation Contract is null ! Any solution?

You'd need to identify the Soap message expected by Client code and correct code to be in synch with Soap message being returned by service.

In this example, you can identify How Client expects response message to be formatted by following,

a. creating Service stub from WSDL using WSDL /serverInterface
b. Create a class implimenting the Service stub
c. Host the WebService in IIS and browse the help page
d. Click on operation called from list.
e. Help page shows the expected request and response soap message

1 Comment

in this case, that won't (quite) show the underlying expected format... unless you can decode/encode protobufs in your head :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.