Exposing Additional Types in WSDL with Known Types

Crypto Ultimatum

How to Make Money with Bitcoin

Get Instant Access

Data types are exposed in WSDL if they meet any of the conditions described earlier. There are additional cases, however, when you would also like to force a type to be included in the WSDL contract.

One example is class hierarchies. If a serialized derived class arrives at an endpoint that is expecting the serialized base class, WCF will not know how to deserialize the class because the derived class was not part of the contract. Another example is a hashtable class, which stores other classes as its element. The WSDL will define the hashtable class, but not the classes contained in the hashtable.

In these cases, you must tell WCF about the classes that should explicitly be included in the WSDL contract. This is done with KnownTypes. It can be done in four ways: by adding the KnownType attribute to a [DataContract], by the attribute in the [ServiceContract] or [OperationContract], by adding a reference to it and its assembly into the configuration, or by determining it when generating the WSDL.

Listing 2.20 shows a data contract that defines a base class, Price, and two classes derived from the base class, StockPrice and MetalPrice. Note the [KnownType] attribute on the data contract. This tells WCF to include the XSD representation of StockPrice and MetalPrice in the WSDL when exposing the contract. The listing also contains an implementation of the service. The GetPrice operation is polymorphic and returns either a StockPrice or MetalPrice type, depending on what was requested. The client code that calls GetPrice through the proxy must cast the result to the expected type to access the return value class.

Listing 2.20 KnownType Defined in a Data Contract using System;

using System.ServiceModel;

using System.Runtime.Serialization;

namespace EssentialWCF

[DataContract(Namespace = "http://EssentialWCF/")]

[KnownType(typeof(StockPrice))]

[KnownType(typeof(MetalPrice))]

public class Price {

[DataMember] public double CurrentPrice;

[DataMember] public DateTime CurrentTime;

[DataMember] public string Currency;

[DataContract(Namespace = "http://EssentialWCF/")]

public class StockPrice : Price {

[DataMember] public string Ticker;

[DataMember] public long DailyVolume;

[DataContract(Namespace = "http://EssentialWCF/")]

public class MetalPrice : Price {

[DataMember] public string Metal;

[DataMember] public string Quality;

[ServiceBehavior (Namespace="http://EssentialWCF/FinanceService/")] [ServiceContract (Namespace="http://EssentialWCF/FinanceService/")]

public class StockService {

[OperationContract]

private Price GetPrice(string id, string type) {

StockPrice s = new StockPrice(); s.Ticker = id; s.DailyVolume = 45000000; s.CurrentPrice = 94.15; s.CurrentTime = System.DateTime.Now; s.Currency = "USD"; return s;

MetalPrice g = new MetalPrice(); g.Metal = id; g.Quality = "0.999"; g.CurrentPrice = 785.00; g.CurrentTime = System.DateTime.Now; g.Currency = "USD"; return g;

return new Price();

Alternatively, you can define the KnownType at the OperationContract level with the [ServiceKnownType] attribute. When KnownTypes are defined at the Operation level, the derived types can be used only in the operation that defines the known types. In other words, not all operations in a service can use the derived types. Listing 2.21 shows a snippet of code that uses a [ServiceKnownType] attribute. In this example, a client can call GetPrice and when the message is returned from the service, the deserializer will create a StockPrice or MetalPrice object. But the client can pass only a Price object, not a StockPrice or MetalPrice, when calling SetPrice, because the serializer will not know how to represent those derived types in XML.

Listing 2.21 KnownType Defined in an Operation Contract

[ServiceBehavior (Namespace="http://EssentialWCF/FinanceService/")] [ServiceContract (Namespace="http://EssentialWCF/FinanceService/")]

public class StockService {

[ServiceKnownType(typeof(StockPrice))] [ServiceKnownType(typeof(MetalPrice))]

[OperationContract]

private Price GetPrice(string id, string type) {

[OperationContract]

Void SetPrice(Price p) {

The disadvantage of defining known types in code, whether at the data contract or service contract level, is that you need to know the universe of derived types at compile time. If a new type is added, you need to recompile the code. This can be resolved with two methods.

First, you can move the known type reference from code to configuration and include known type information in the system. runtime. serialization section of the service configuration file. Respecting the class hierarchy, you need to add a reference to the base class and then add knownType references to the derived classes. This is shown in Listing 2.22, where EssentialWCF. Price is the base class and EssentialWCF.StockPrice and EssentialWCF. MetalPrice are the derived classes. StockService is the DLL hosting these types.

Listing 2.22 KnownType Defined in Configuration

<system.runtime.serialization> <dataContractSerializer> <declaredTypes>

<add type="EssentialWCF.Price, StockService, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"> <knownType type="EssentialWCF.StockPrice, StockService, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/> <knownType type="EssentialWCF.MetalPrice, StockService, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>

</add> </declaredTypes> </dataContractSerializer> </system.runtime.serialization>

The most general solution for specifying the derived types in the contract is to generate it at runtime. This can be done thanks to some hooks exposed in WCF. The constructor of both the [KnownType] attribute and [ServiceKnownType] attribute accepts a string parameter. This string is a method name that is called at serialization or deserialization time to return a list of known types. If you use a metadata repository, you could look up type information from the repository or database and expose the types at runtime. Listing 2.23 shows a simpler implementation, where the type names are hardcoded in the GetKnownTypes method rather than being pulled from an external repository.

Listing 2.23 KnownType Defined in Code at Runtime

[DataContract(Namespace = "http://EssentialWCF/")] [KnownType("GetKnownTypes")]

public class Price {

[DataMember] public double CurrentPrice;

[DataMember] public DateTime CurrentTime;

[DataMember] public string Currency;

static Type[] GetKnownTypes()

return new Type[] { typeof(StockPrice)j typeof(MetalPrice) };

Listing 2.23 continued

[DataContract(Namespace = "http://EssentialWCF/")]

public class StockPrice : Price {

[DataMember] public string Ticker;

[DataMember] public long DailyVolume;

[DataContract(Namespace = "http://EssentialWCF/")]

public class MetalPrice : Price {

[DataMember] public string Metal; [DataMember] public string Quality;

Was this article helpful?

0 -1

Post a comment