Reputation: 33
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));
}
}
}
Upvotes: 2
Views: 2262
Reputation: 2123
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
Upvotes: 0
Reputation: 1063338
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.
Upvotes: 1