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));
        }
    }
}
  • 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 – Marc Gravell May 25 '11 at 23:04
  • I posted the code generated for TestClass above. – Andy Trimpe May 26 '11 at 15:12

2 Answers2

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.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • 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 Marsh May 26 '11 at 17:18
  • @Drew and for this dual-purpose, protobuf-net is perfectly happy working from DataContract/DataMember attributes :) – Marc Gravell May 26 '11 at 17:53
  • @Drew - however, that interface is a new one to me; I will take a look – Marc Gravell May 26 '11 at 17:53
  • @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. – Drew Marsh May 26 '11 at 18:11
  • @Andy share assembles are a breeze for this - by far the easiest option for what is already a pretty custom scenario – Marc Gravell May 26 '11 at 18:38
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
Community
  • 1
  • 1
amit
  • 2,093
  • 16
  • 10
  • in this case, that *won't* (quite) show the underlying expected format... unless you can decode/encode protobufs in your head :) – Marc Gravell May 25 '11 at 23:06