Search code examples
c#.netwcfwcf-configurationknown-types

WCF Known Type from System.Object in Config


I'm trying to specify a known type in my config, but I'm having problems with the fact that it derives from Object. I can make it work specifying the known type via attribute. But in this case I need to make it work from the config.

Here's an example. The following works fine:

[ServiceContract]
[ServiceKnownType(typeof(MyData))]
public interface IContract
{
    [OperationContract]
    void Send(object data);
}

[DataContract]
public class MyData
{
    [DataMember]
    public string Message { get; set; }
}

But if I remove the ServiceKnownType attribute and put the following in the config:

<system.runtime.serialization>
  <dataContractSerializer>
    <declaredTypes>
      <add type="System.Object, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
        <knownType type="WpfApplication1.MyData, WpfApplication1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
      </add>
    </declaredTypes>
  </dataContractSerializer>
</system.runtime.serialization>

I get a ConfigurationErrorsException with the message "The value for the property 'type' is not valid. The error is: The type System.Object cannot be used as a declared type in config."

Is there anyway to make this work via config?


Solution

  • The answer turns out to be it's not possible to do what I want to do in the config file alone. The config above corresponds to the [KnownType] attribute used on DataContracts. There appears to be no way to implement [ServiceKnownType] in the config.

    An alternate approach is to use [ServiceKnownType(methodName, type)] attribute with a custom configuration section. The new config looks like this:

    <configuration>
      <configSections>
        <section
          name="serviceKnownTypes"
          type="WpfApplication1.ServiceKnownTypesSection, WpfApplication1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
      </configSections>
      <serviceKnownTypes>
        <declaredServices>
          <serviceContract type="WpfApplication1.IContract, WpfApplication1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
            <knownTypes>
              <knownType type="WpfApplication1.MyData, WpfApplication1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
            </knownTypes>
          </serviceContract>
        </declaredServices>
      </serviceKnownTypes>
    </configuration>
    

    The contracts:

    [ServiceContract]
    [ServiceKnownType("GetServiceKnownTypes", typeof(KnownTypeHelper))]
    public interface IContract
    {
        [OperationContract]
        void Send(object data);
    }
    
    [DataContract]
    public class MyData
    {
        [DataMember]
        public string Message { get; set; }
    }
    

    The helper class that contains the callback that returns the list of known types

    public static class KnownTypeHelper
    {
        public static IEnumerable<Type> GetServiceKnownTypes(ICustomAttributeProvider provider)
        {
            List<Type> result = new List<Type>();
    
            ServiceKnownTypesSection serviceKnownTypes = (ServiceKnownTypesSection)ConfigurationManager.GetSection("serviceKnownTypes");
            DeclaredServiceElement service = serviceKnownTypes.Services[((Type)(provider)).AssemblyQualifiedName];
    
            foreach (ServiceKnownTypeElement knownType in service.KnownTypes)
            {
                result.Add(knownType.Type);
            }
    
            return result;
        }
    }
    

    Information on creating custom config sections can be found here:

    http://msdn.microsoft.com/en-us/library/2tw134k3.aspx

    http://msdn.microsoft.com/en-us/library/system.configuration.configurationcollectionattribute.aspx