Search code examples
c#.netwcfserializationnetdatacontractserializer

Passing Guid to WCF service with NetDataContractSerializer


I've created the contract

[ServiceContract]
public interface ITestService
{
    [OperationContract]
    [NetDataContract]
    void PassGuid(Guid id);

    [OperationContract]
    [NetDataContract]
    void PassInt(int id);
}

Attribute NetDataContractAttribute described here. When I'm calling PassGuid I get an error:

Additional information: The formatter threw an exception while trying to deserialize the message: There was an error while trying to deserialize parameter http://tempuri.org/:id. The InnerException message was 'XML 'Element' 'http://tempuri.org/:id' does not contain expected attribute 'http://schemas.microsoft.com/2003/10/Serialization/:Type'. The deserializer has no knowledge of which type to deserialize. Check that the type being serialized has the same contract as the type being deserialized.'. Please see InnerException for more details.

But PassInt could be called with no errors.

Full source code here.

Edited: This question is not about efficiency of sending guids, like this one, it is mostly about using NetDataContractSerializer.


Solution

  • You are using the custom serializer as NetDataContractSerializer at server side. The difference between the NetDataContractSerializer and DataContractSerializer is that the serialization adds additional information of Types.

    E.g. DatacontractsSerializer serialized to:

    <Customer xmlns="http://www.contoso.com" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
      <FirstName>Zighetti</FirstName>
      <ID>101</ID>
      <LastName>Barbara</LastName>
    </Customer>
    

    NetDataContractSerializer would serialize the same object to:

    <Customer z:Id="1" z:Type="NetDCS.Person" z:Assembly="NetDCS, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" xmlns="http://www.contoso.com" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">
      <FirstName z:Id="2">Zighetti</FirstName>
      <ID>101</ID>
      <LastName z:Id="3">Barbara</LastName>
    </Customer>
    

    Inducing more type specific information.

    In your case server knows about the NetDataContractSerializer but client doesn't. You have to share this behavior to the client by putting it in a shared code library. Then you can add this enw behavior to the client using below code:

           using (var client = new TestServiceClient())
            {
                // Get the channel factory
                var factory = client.ChannelFactory;
    
                // Attach the behavior
                foreach (OperationDescription desc in factory.Endpoint.Contract.Operations)
                {
                    DataContractSerializerOperationBehavior dcsOperationBehavior = desc.Behaviors.Find<DataContractSerializerOperationBehavior>();
                    if (dcsOperationBehavior != null)
                    {
                        int idx = desc.Behaviors.IndexOf(dcsOperationBehavior);
                        desc.Behaviors.Remove(dcsOperationBehavior);
                        desc.Behaviors.Insert(idx, new WcfClient.NetDataContractAttribute.NetDataContractSerializerOperationBehavior(desc));
                        //return true;
    
                    }
                }
    
                // pass
                client.PassInt(0);
                // fail
                client.PassGuid(Guid.NewGuid());
                client.Close();
            }
    

    You can also use attach the behavior via configuration file.