Known Types

Download sample

This sample demonstrates how to specify information about derived types in a data contract. Data contracts allow you to pass structured data to and from services. In object-oriented programming, a type that inherits from another type can be used in place of the original type. In service-oriented programming, schemas rather than types are communicated and therefore, the relationship between types is not preserved. The KnownTypeAttribute attribute allows information about derived types to be included in the data contract. If this mechanism is not used, a derived type cannot be sent or received where a base type is expected.

Note

The WCF samples may already be installed on your machine. Check for the following (default) directory before continuing.

<InstallDrive>:\Samples\WCFWFCardspace

If this directory does not exist, click the download sample link at the top of this page. Note that this link downloads and installs all of the WF, WCF, and CardSpace samples. The sample is located in the following directory.

<InstallDrive>:\Samples\WCFWFCardSpace\WCF\Basic\Contract\Data\KnownTypes

Note

The set-up procedure and build instructions for this sample are located at the end of this topic.

The service contract for the service uses complex numbers, as shown in the following sample code.

// Define a service contract.
[ServiceContract(Namespace="http://Microsoft.ServiceModel.Samples")]
public interface ICalculator
{
    [OperationContract]
    ComplexNumber Add(ComplexNumber n1, ComplexNumber n2);
    [OperationContract]
    ComplexNumber Subtract(ComplexNumber n1, ComplexNumber n2);
    [OperationContract]
    ComplexNumber Multiply(ComplexNumber n1, ComplexNumber n2);
    [OperationContract]
    ComplexNumber Divide(ComplexNumber n1, ComplexNumber n2);
}

The DataContractAttribute and DataMemberAttribute is applied to the ComplexNumber class to indicate which fields of the class can be passed between the client and the service. The derived ComplexNumberWithMagnitude class can be used in place of ComplexNumber. The KnownTypeAttribute attribute on the ComplexNumber type indicates this.

[DataContract(Namespace="http://Microsoft.ServiceModel.Samples")]
[KnownType(typeof(ComplexNumberWithMagnitude))]
public class ComplexNumber
{
    [DataMember]
    public double Real = 0.0D;
    [DataMember]
    public double Imaginary = 0.0D;

    public ComplexNumber(double real, double imaginary)
    {
        this.Real = real;
        this.Imaginary = imaginary;
    }
}

The ComplexNumberWithMagnitude type derives from ComplexNumber but adds an additional data member, Magnitude.

[DataContract(Namespace="http://Microsoft.ServiceModel.Samples")]
public class ComplexNumberWithMagnitude : ComplexNumber
{
    public ComplexNumberWithMagnitude(double real, double imaginary) :        base(real, imaginary) { }

    [DataMember]
    public double Magnitude
    {
        get { return Math.Sqrt(Imaginary*Imaginary  + Real*Real); }
        set { throw new NotImplementedException(); }
    }
}

To demonstrate the known types feature, the service is implemented in such a way that it returns a ComplexNumberWithMagnitude only for addition and subtraction. (Even though the contract specifies ComplexNumber, this is allowed because of the KnownTypeAttribute attribute). Multiplication and division still return the base ComplexNumber type.

public class DataContractCalculatorService : IDataContractCalculator
{
    public ComplexNumber Add(ComplexNumber n1, ComplexNumber n2)
    {
        //Return the derived type.
        return new ComplexNumberWithMagnitude(n1.Real + n2.Real,
                                      n1.Imaginary + n2.Imaginary);
    }

    public ComplexNumber Subtract(ComplexNumber n1, ComplexNumber n2)
    {
        //Return the derived type.
        return new ComplexNumberWithMagnitude(n1.Real - n2.Real, 
                                 n1.Imaginary - n2.Imaginary);
    }

    public ComplexNumber Multiply(ComplexNumber n1, ComplexNumber n2)
    {
        double real1 = n1.Real * n2.Real;
        double imaginary1 = n1.Real * n2.Imaginary;
        double imaginary2 = n2.Real * n1.Imaginary;
        double real2 = n1.Imaginary * n2.Imaginary * -1;
        //Return the base type.
        return new ComplexNumber(real1 + real2, imaginary1 + 
                                                  imaginary2);
    }

    public ComplexNumber Divide(ComplexNumber n1, ComplexNumber n2)
    {
        ComplexNumber conjugate = new ComplexNumber(n2.Real, 
                                     -1*n2.Imaginary);
        ComplexNumber numerator = Multiply(n1, conjugate);
        ComplexNumber denominator = Multiply(n2, conjugate);
        //Return the base type.
        return new ComplexNumber(numerator.Real / denominator.Real,
                                             numerator.Imaginary);
    }
}

On the client, both the service contract and the data contract are defined in the source file generatedClient.cs, which is generated by the ServiceModel Metadata Utility Tool (Svcutil.exe) from service metadata. Because the KnownTypeAttribute attribute is specified in the service's data contract, the client is able to receive both the ComplexNumber and ComplexNumberWithMagnitude classes when using the service. The client detects whether it got a ComplexNumberWithMagnitude and generate the appropriate output:

// Create a client
DataContractCalculatorClient client =     new DataContractCalculatorClient();

// Call the Add service operation.
ComplexNumber value1 = new ComplexNumber(); value1.real = 1; value1.imaginary = 2;
ComplexNumber value2 = new ComplexNumber(); value2.real = 3; value2.imaginary = 4;
ComplexNumber result = client.Add(value1, value2);
Console.WriteLine("Add({0} + {1}i, {2} + {3}i) = {4} + {5}i",
    value1.real, value1.imaginary, value2.real, value2.imaginary,    result.real, result.imaginary);
if (result is ComplexNumberWithMagnitude)
{
    Console.WriteLine("Magnitude: {0}",         ((ComplexNumberWithMagnitude)result).Magnitude);
}
else
{
    Console.WriteLine("No magnitude was sent from the service");
}

When you run the sample, the requests and responses of the operation are displayed in the client console window. Note that a magnitude is printed for addition and subtraction but not for multiplication and division because of the way the service was implemented. Press ENTER in the client window to shut down the client.

Add(1 + 2i, 3 + 4i) = 4 + 6i
Magnitude: 7.21110255092798
Subtract(1 + 2i, 3 + 4i) = -2 + -2i
Magnitude: 2.82842712474619
Multiply(2 + 3i, 4 + 7i) = -13 + 26i
No magnitude was sent from the service
Divide(3 + 7i, 5 + -2i) = 0.0344827586206897 + 41i
No magnitude was sent from the service

    Press <ENTER> to terminate client.

To set up, build, and run the sample

  1. Ensure that you have performed the One-Time Set Up Procedure for the Windows Communication Foundation Samples.

  2. To build the C# or Visual Basic .NET edition of the solution, follow the instructions in Building the Windows Communication Foundation Samples.

  3. To run the sample in a single- or cross-machine configuration, follow the instructions in Running the Windows Communication Foundation Samples.

© 2007 Microsoft Corporation. All rights reserved.