wcfdatacontractknown-types

Attributes on a derived type not being deserialized in a WCF client even though KnownType is used


I have the following types:

public enum MyEnum
{
    Value1,
    Value2
}

[DataContract]
public class Configuration
{
    [DataMember]
    public MyEnum MyValue { get; set; }
    [DataMember]
    public Credentials CredentialValues { get; set; }
}

[DataContract, KnownType(typeof(CustomCredentials))]
public class Credentials 
{

}

[DataContract]
public class CustomCredentials : Credentials 
{
    [DataMember]
    public string Property1 { get; set; }
    [DataMember]
    public string Property2 { get; set; }
}

And on my service interface, I have a function that returns an instance of Configuration with its CredentialValues property set to a fully populated instance of CustomCredentials. I receive no errors from the client or the server, but while the data is being property serialized on the server and received by the client, the properties on CustomCredentials never have a value. What do I need to change here in order to have these properties properly deserialized on the client?

For reference, the connection between client and server is made with a DuplexChannelFactory over a NetTcpBinding using a data/service contract project that is shared by the client and service applications (the service is self-hosted), so there are no service proxy types that could need to be regenerated.


Solution

  • Added this code to the Contracts project along with your DataContracts.

    [ServiceContract(Namespace = "http://schemas.platinumray.com/duplex", SessionMode = SessionMode.Required, CallbackContract = typeof(IService1Callback))]
    public interface IService1
    {
        [OperationContract(IsOneWay = true)]
        void GetData();
    }
    
    public interface IService1Callback
    {
        [OperationContract(IsOneWay = true)]
        void SetData(Configuration config);
    }
    

    Created the service.

    public class Service1 : IService1
    {
        public void GetData()
        {
            var x = new Configuration()
            {
                MyValue = MyEnum.Value1,
                CredentialValues = new CustomCredentials { Property1 = "Something", Property2 = "Something else" }
            };
            OperationContext.Current.GetCallbackChannel<IService1Callback>().SetData(x);
        }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
    
            using (ServiceHost host = new ServiceHost( typeof(Service1), new Uri[] { new Uri("net.tcp://localhost:6789") }))
            {
                host.AddServiceEndpoint(typeof(IService1), new NetTcpBinding(), "Service1");
                host.Open();
                Console.ReadLine();
                host.Close();
            }
        }
    }
    

    Created the client.

    public class CallbackHandler : IService1Callback
    {
        public void SetData(Configuration config)
        {
            Console.WriteLine(config.CredentialValues.GetType().Name);
            Console.WriteLine(((CustomCredentials)config.CredentialValues).Property1);
            Console.WriteLine(((CustomCredentials)config.CredentialValues).Property2);
        }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            // Setup the client
            var callbacks = new CallbackHandler();
            var endpoint = new EndpointAddress(new Uri("net.tcp://localhost:6789/Service1"));
            using (var factory = new DuplexChannelFactory<IService1>(callbacks, new NetTcpBinding(), endpoint))
            {
                var client = factory.CreateChannel();
                client.GetData();
                Console.ReadLine();
                factory.Close();
            }
        }
    }
    

    Outputs the following as expected:

    CustomCredentials
    Something
    Something else
    

    So this actually worked without modifying any of your data contracts... The same results if I revert to a twoway operation and just return Configuration directly without using the callback.

    Also tried making Credentials abstract but could not replicate your problem.

    Have I missed something?