WCF: Configuring Known Types
Asked Answered
K

3

6

I want to know as to how to configure known types in WCF. For example, I have a Person class and an Employee class. The Employee class is a sublass of the Person class. Both class are marked with a [DataContract] attribute.

I dont want to hardcode the known type of a class, like putting a [ServiceKnownType(typeof(Employee))] at the Person class so that WCF will know that Employee is a subclass of Person.

Now, I added to the host's App.config the following XML configuration:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.runtime.serialization>
    <dataContractSerializer>
      <declaredTypes>
        <add type="Person, WCFWithNoLibrary, Version=1.0.0.0,Culture=neutral,PublicKeyToken=null">
          <knownType type="Employee, WCFWithNoLibrary, Version=1.0.0.0,Culture=neutral, PublicKeyToken=null" />
        </add>
      </declaredTypes>
    </dataContractSerializer>
  </system.runtime.serialization>
  <system.serviceModel>
    ....... 
  </system.serviceModel>
</configuration>

I compiled it, run the host, added a service reference at the client and added some code and run the client. But an error occured:

The formatter threw an exception while trying to deserialize the message: There was an error while trying to deserialize parameter http://www.herbertsabanal.net:person. The InnerException message was 'Error in line 1 position 247. Element 'http://www.herbertsabanal.net:person' contains data of the 'http://www.herbertsabanal.net/Data:Employee' data contract. The deserializer has no knowledge of any type that maps to this contract. Add the type corresponding to 'Employee' to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding it to the list of known types passed to DataContractSerializer.'. Please see InnerException for more details.

Below are the data contracts:

[DataContract(Namespace="http://www.herbertsabanal.net/Data", Name="Person")]
    class Person
    {
        string _name;
        int _age;

        [DataMember(Name="Name", Order=0)]
        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }

        [DataMember(Name="Age", Order=1)]
        public int Age
        {
            get { return _age; }
            set { _age = value; }
        }
    }


[DataContract(Namespace="http://www.herbertsabanal.net/Data", Name="Employee")]
    class Employee : Person
    {
        string _id;

        [DataMember]
        public string ID
        {
            get { return _id; }
            set { _id = value; }
        }
    }

Btw, I didn't use class libraries (WCF class libraries or non-WCF class libraries) for my service. I just plain coded it in the host project.

I guess there must be a problem at the config file (please see config file above). Or I must be missing something. Any help would be pretty much appreciated.

Kapor answered 18/2, 2009 at 8:10 Comment(1)
i suspect that the configuration works only if using a third-party library, referencing it to the service. i'll try it and i'll get back to this question if i find some answers.Kapor
K
8

I guess I have found the answer now.

The configuration file I posted above looks like this:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.runtime.serialization>
    <dataContractSerializer>
      <declaredTypes>
        <add type="Person, WCFWithNoLibrary, Version=1.0.0.0,Culture=neutral,PublicKeyToken=null">
          <knownType type="Employee, WCFWithNoLibrary, Version=1.0.0.0,Culture=neutral, PublicKeyToken=null" />
        </add>
      </declaredTypes>
    </dataContractSerializer>
  </system.runtime.serialization>
  <system.serviceModel>
    ....... 
  </system.serviceModel>
</configuration>

What I just added was, the Namespace of the Person class and the Employee class. And no need for the longer Version and Culture values.... The correct configuration should be:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.runtime.serialization>
    <dataContractSerializer>
      <declaredTypes>
        <add type="WCFWithNoLibrary.Person, WCFWithNoLibrary">
          <knownType type="WCFWithNoLibrary.Employee, WCFWithNoLibrary" />
        </add>
      </declaredTypes>
    </dataContractSerializer>
  </system.runtime.serialization>
  <system.serviceModel>
    ....... 
  </system.serviceModel>
</configuration>

Now it is shorter and makes more sense. But if 3rd party libraries are used, then adding version, culture, publickeytokens would be required.

Kapor answered 18/2, 2009 at 8:53 Comment(2)
Upvote. Did you have to do anything on the Client side .config ?Quinary
You will need to duplicate the configuration on the client so it can receive the new type.Flit
T
6

I know this was answered a long time ago, but, another (maybe more obvious for future programmers) solution:

[KnownType(typeof(SubClass))]
public class BaseClass

Scott

Television answered 17/3, 2011 at 14:46 Comment(1)
this is not always possible if the subtype is in a different projectHardiman
A
0

I got this lengthy error message also in another case. I did use the KnownTypeAttribute and had successfully deployed an application which uses WCF.RIA to production. In the second release I added a new subtype, and added the necessary corresponding KnownTypeAttribute (the compiler did not accept it without this attribute - great). What the compiler did accept and what ran on my machine, did not run in production, however. Only in production I got the error mentioned above. Comparing all the uses of the existing subtypes and the new one revealed I had forgotten that WCF.RIA requires the name of the subtype to be used in a name of a method, like GetMySubTypes. So if you get this error after having added the attributes, see whether it's because of WCF.RIAs conventions.

Autoerotism answered 21/4, 2011 at 14:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.