次の方法で共有


既知のデータ コントラクト型

KnownTypeAttribute クラスを使用すると、逆シリアル化時に考慮する必要がある型を事前に指定できます。 作業例については、「 既知の型 」の例を参照してください。

通常、クライアントとサービスの間でパラメーターと戻り値を渡すとき、両方のエンドポイントは、送信されるデータのすべてのデータ コントラクトを共有します。 ただし、次の状況では、これは当たりません。

  • 送信されたデータ コントラクトは、想定されるデータ コントラクトから派生します。 詳細については、「 データ コントラクトの等価性」の継承に関するセクションを参照してください。 その場合、送信されるデータは、受信エンドポイントで想定されるデータ コントラクトと同じではありません。

  • 送信される情報の宣言された型は、クラス、構造体、または列挙型ではなく、インターフェイスです。 そのため、インターフェイスを実装する型が実際に送信されるのを事前に把握できないため、受信エンドポイントは、送信されたデータのデータ コントラクトを事前に判断できません。

  • 送信する情報の宣言された型が Object。 すべての型は Objectから継承され、実際に送信される型を事前に把握できないため、受信エンドポイントは送信されたデータのデータ コントラクトを事前に判断できません。 これは、最初の項目の特殊なケースです。すべてのデータ コントラクトは、既定の空のデータ コントラクトから派生し、 Object用に生成されます。

  • .NET Framework 型を含む一部の型には、上記の 3 つのカテゴリのいずれかに属するメンバーがあります。 たとえば、 Hashtable では、 Object を使用して、実際のオブジェクトをハッシュ テーブルに格納します。 これらの型をシリアル化する場合、受信側は、これらのメンバーのデータ コントラクトを事前に決定できません。

KnownTypeAttribute クラス

受信エンドポイントにデータが到着すると、WCF ランタイムは共通言語ランタイム (CLR) 型のインスタンスにデータを逆シリアル化しようとします。 逆シリアル化のためにインスタンス化される型は、最初に受信メッセージを調べて、メッセージの内容が準拠するデータ コントラクトを決定することによって選択されます。 逆シリアル化エンジンは、メッセージの内容と互換性のあるデータ コントラクトを実装する CLR 型の検索を試みます。 このプロセス中に逆シリアル化エンジンで許可される候補型のセットは、デシリアライザーの "既知の型" のセットと呼ばれます。

逆シリアル化エンジンに型について知らせる方法の 1 つは、 KnownTypeAttributeを使用することです。 この属性は、個々のデータ メンバーには適用できず、データ コントラクト型全体にのみ適用できます。 属性は、クラスまたは構造体にすることができる 外部型 に適用されます。 最も基本的な使用法では、属性を適用すると、型が "既知の型" として指定されます。これにより、外部型のオブジェクトまたはそのメンバーを介して参照されるオブジェクトが逆シリアル化されるたびに、既知の型が既知の型のセットの一部になります。 同じ型に複数の KnownTypeAttribute 属性を適用できます。

既知の型とプリミティブ

プリミティブ型だけでなく、プリミティブとして扱われる特定の型 ( DateTimeXmlElementなど) は常に "既知" であり、このメカニズムを介して追加する必要はありません。 ただし、プリミティブ型の配列を明示的に追加する必要があります。 ほとんどのコレクションは配列と同等と見なされます。 (非ジェネリック コレクションは、 Objectの配列と同等と見なされます)。 プリミティブ、プリミティブ配列、およびプリミティブコレクションの利用例については、例 4 を参照してください。

他のプリミティブ型とは異なり、 DateTimeOffset 構造体は既定では既知の型ではないため、既知の型の一覧に手動で追加する必要があります。

例示

次の例は、使用中の KnownTypeAttribute クラスを示しています。

例 1

継承関係を持つ 3 つのクラスがあります。

[DataContract]
public class Shape { }

[DataContract(Name = "Circle")]
public class CircleType : Shape { }

[DataContract(Name = "Triangle")]
public class TriangleType : Shape { }
<DataContract()> _
Public Class Shape
End Class

<DataContract(Name:="Circle")> _
Public Class CircleType
    Inherits Shape
End Class
<DataContract(Name:="Triangle")> _
Public Class TriangleType
    Inherits Shape
End Class

次の CompanyLogo クラスはシリアル化できますが、逆シリアル化エンジンがデータ コントラクト名 "Circle" または "Triangle" を持つ型を認識しないため、 ShapeOfLogo メンバーが CircleType または TriangleType オブジェクトに設定されている場合は逆シリアル化できません。

[DataContract]
public class CompanyLogo
{
    [DataMember]
    private Shape ShapeOfLogo;
    [DataMember]
    private int ColorOfLogo;
}
<DataContract()> _
Public Class CompanyLogo
    <DataMember()> _
    Private ShapeOfLogo As Shape
    <DataMember()> _
    Private ColorOfLogo As Integer
End Class

CompanyLogo型を正しく記述する方法を次のコードに示します。

[DataContract]
[KnownType(typeof(CircleType))]
[KnownType(typeof(TriangleType))]
public class CompanyLogo2
{
    [DataMember]
    private Shape ShapeOfLogo;
    [DataMember]
    private int ColorOfLogo;
}
<DataContract(), KnownType(GetType(CircleType)), KnownType(GetType(TriangleType))> _
Public Class CompanyLogo2
    <DataMember()> _
    Private ShapeOfLogo As Shape
    <DataMember()> _
    Private ColorOfLogo As Integer
End Class

外部型 CompanyLogo2 が逆シリアル化されるたびに、逆シリアル化エンジンは CircleTypeTriangleType について認識するため、"Circle" データ コントラクトと "Triangle" データ コントラクトの一致する型を見つけることができます。

例 2

次の例では、CustomerTypeACustomerTypeBの両方にCustomerデータ コントラクトがある場合でも、逆シリアル化エンジンにCustomerTypeBのみが認識されるため、PurchaseOrderが逆シリアル化されるたびにCustomerTypeBのインスタンスが作成されます。

public interface ICustomerInfo
{
    string ReturnCustomerName();
}

[DataContract(Name = "Customer")]
public class CustomerTypeA : ICustomerInfo
{
    public string ReturnCustomerName()
    {
        return "no name";
    }
}

[DataContract(Name = "Customer")]
public class CustomerTypeB : ICustomerInfo
{
    public string ReturnCustomerName()
    {
        return "no name";
    }
}

[DataContract]
[KnownType(typeof(CustomerTypeB))]
public class PurchaseOrder
{
    [DataMember]
    ICustomerInfo buyer;

    [DataMember]
    int amount;
}
Public Interface ICustomerInfo
    Function ReturnCustomerName() As String
End Interface

<DataContract(Name:="Customer")> _
Public Class CustomerTypeA
    Implements ICustomerInfo
    Public Function ReturnCustomerName() _
    As String Implements ICustomerInfo.ReturnCustomerName
        Return "no name"
    End Function
End Class

<DataContract(Name:="Customer")> _
Public Class CustomerTypeB
    Implements ICustomerInfo
    Public Function ReturnCustomerName() _
    As String Implements ICustomerInfo.ReturnCustomerName
        Return "no name"
    End Function
End Class

<DataContract(), KnownType(GetType(CustomerTypeB))> _
Public Class PurchaseOrder
    <DataMember()> _
    Private buyer As ICustomerInfo

    <DataMember()> _
    Private amount As Integer
End Class

例 3

次の例では、 Hashtable はコンテンツを内部的に Objectとして格納します。 ハッシュ テーブルを正常に逆シリアル化するには、逆シリアル化エンジンが、そこで発生する可能性のある型のセットを認識している必要があります。 この場合、 Book オブジェクトと Magazine オブジェクトのみが Catalogに格納されるため、 KnownTypeAttribute 属性を使用して追加されます。

[DataContract]
public class Book { }

[DataContract]
public class Magazine { }

[DataContract]
[KnownType(typeof(Book))]
[KnownType(typeof(Magazine))]
public class LibraryCatalog
{
    [DataMember]
    System.Collections.Hashtable theCatalog;
}
<DataContract()> _
Public Class Book
End Class

<DataContract()> _
Public Class Magazine
End Class

<DataContract(), KnownType(GetType(Book)), KnownType(GetType(Magazine))> _
Public Class LibraryCatalog
    <DataMember()> _
    Private theCatalog As System.Collections.Hashtable
End Class

例 4

次の例では、データ コントラクトは数値と数値に対して実行する操作を格納します。 Numbers データ メンバーには、整数、整数の配列、または整数を含むList<T>を指定できます。

注意事項

これは、SVCUTIL.EXEを使用して WCF プロキシを生成する場合にのみ、クライアント側で機能します。 SVCUTIL.EXEは、既知の型を含むメタデータをサービスから取得します。 この情報がないと、クライアントは型を逆シリアル化できません。

[DataContract]
[KnownType(typeof(int[]))]
public class MathOperationData
{
    private object numberValue;
    [DataMember]
    public object Numbers
    {
        get { return numberValue; }
        set { numberValue = value; }
    }
    //[DataMember]
    //public Operation Operation;
}
<DataContract(), KnownType(GetType(Integer()))> _
Public Class MathOperationData
    Private numberValue As Object

    <DataMember()> _
    Public Property Numbers() As Object
        Get
            Return numberValue
        End Get
        Set(ByVal value As Object)
            numberValue = value
        End Set
    End Property
End Class

これがアプリケーション コードです。

// This is in the service application code:
static void Run()
{

    MathOperationData md = new MathOperationData();

    // This will serialize and deserialize successfully because primitive
    // types like int are always known.
    int a = 100;
    md.Numbers = a;

    // This will serialize and deserialize successfully because the array of
    // integers was added to known types.
    int[] b = new int[100];
    md.Numbers = b;

    // This will serialize and deserialize successfully because the generic
    // List<int> is equivalent to int[], which was added to known types.
    List<int> c = new List<int>();
    md.Numbers = c;
    // This will serialize but will not deserialize successfully because
    // ArrayList is a non-generic collection, which is equivalent to
    // an array of type object. To make it succeed, object[]
    // must be added to the known types.
    ArrayList d = new ArrayList();
    md.Numbers = d;
}
' This is in the service application code:
Shared Sub Run()
    Dim md As New MathOperationData()
    ' This will serialize and deserialize successfully because primitive 
    ' types like int are always known.
    Dim a As Integer = 100
    md.Numbers = a

    ' This will serialize and deserialize successfully because the array of 
    ' integers was added to known types.
    Dim b(99) As Integer
    md.Numbers = b

    ' This will serialize and deserialize successfully because the generic 
    ' List(Of Integer) is equivalent to Integer(), which was added to known types.
    Dim c As List(Of Integer) = New List(Of Integer)()
    md.Numbers = c
    ' This will serialize but will not deserialize successfully because 
    ' ArrayList is a non-generic collection, which is equivalent to 
    ' an array of type object. To make it succeed, object[]
    ' must be added to the known types.
    Dim d As New ArrayList()
    md.Numbers = d

End Sub

既知の型、継承、およびインターフェイス

既知の型が KnownTypeAttribute 属性を使用して特定の型に関連付けられている場合、既知の型は、その型のすべての派生型にも関連付けられます。 たとえば、次のコードを参照してください。

[DataContract]
[KnownType(typeof(Square))]
[KnownType(typeof(Circle))]
public class MyDrawing
{
    [DataMember]
    private object Shape;
    [DataMember]
    private int Color;
}

[DataContract]
public class DoubleDrawing : MyDrawing
{
    [DataMember]
    private object additionalShape;
}
<DataContract(), KnownType(GetType(Square)), KnownType(GetType(Circle))> _
Public Class MyDrawing
    <DataMember()> _
    Private Shape As Object
    <DataMember()> _
    Private Color As Integer
End Class

<DataContract()> _
Public Class DoubleDrawing
    Inherits MyDrawing
    <DataMember()> _
    Private additionalShape As Object
End Class

基底クラス (DoubleDrawing) には既にこれらの属性が適用されているため、KnownTypeAttribute クラスでは、Square フィールドでCircleAdditionalShapeを使用するDrawing属性は必要ありません。

既知の型は、インターフェイスではなく、クラスと構造体にのみ関連付けることができます。

Open Generic メソッドを使用した既知の型

ジェネリック型を既知の型として追加することが必要な場合があります。 ただし、開いているジェネリック型をパラメーターとして KnownTypeAttribute 属性に渡すことはできません。

この問題は、別のメカニズムを使用して解決できます。既知の型コレクションに追加する型の一覧を返すメソッドを記述します。 メソッドの名前は、いくつかの制限があるため、 KnownTypeAttribute 属性の文字列引数として指定されます。

メソッドは、KnownTypeAttribute属性が適用される型に存在する必要があり、静的である必要があり、パラメーターを受け入れず、IEnumerableTypeに割り当てることができるオブジェクトを返す必要があります。

KnownTypeAttribute属性とメソッド名を組み合わせ、同じ型の実際の型を持つKnownTypeAttribute属性を組み合わせることはできません。 さらに、メソッド名を持つ複数の KnownTypeAttribute を同じ型に適用することはできません。

次のクラスを参照してください。

[DataContract]
public class DrawingRecord<T>
{
    [DataMember]
    private T theData;
    [DataMember]
    private GenericDrawing<T> theDrawing;
}
<DataContract()> _
Public Class DrawingRecord(Of T)
    <DataMember()> _
    Private theData As T
    <DataMember()> _
    Private theDrawing As GenericDrawing(Of T)
End Class

theDrawing フィールドには、ジェネリック クラス ColorDrawingとジェネリック クラス BlackAndWhiteDrawingのインスタンスが含まれ、どちらもジェネリック クラス Drawingから継承されます。 通常、両方を既知の型に追加する必要がありますが、次の属性の構文は有効ではありません。

// Invalid syntax for attributes:  
// [KnownType(typeof(ColorDrawing<T>))]  
// [KnownType(typeof(BlackAndWhiteDrawing<T>))]  
' Invalid syntax for attributes:  
' <KnownType(GetType(ColorDrawing(Of T))), _  
' KnownType(GetType(BlackAndWhiteDrawing(Of T)))>  

したがって、これらの型を返すには、メソッドを作成する必要があります。 この型を正しく記述する方法を次のコードに示します。

[DataContract]
[KnownType("GetKnownType")]
public class DrawingRecord2<T>
{
    [DataMember]
    private T TheData;
    [DataMember]
    private GenericDrawing<T> TheDrawing;

    private static Type[] GetKnownType()
    {
        Type[] t = new Type[2];
        t[0] = typeof(ColorDrawing<T>);
        t[1] = typeof(BlackAndWhiteDrawing<T>);
        return t;
    }
}
<DataContract(), KnownType("GetKnownType")> _
Public Class DrawingRecord2(Of T)
    Private TheData As T
    Private TheDrawing As GenericDrawing(Of T)

    Private Shared Function GetKnownType() As Type()
        Dim t(1) As Type
        t(0) = GetType(ColorDrawing(Of T))
        t(1) = GetType(BlackAndWhiteDrawing(Of T))
        Return t
    End Function
End Class

既知の型を追加するその他の方法

さらに、既知の型は構成ファイルを使用して追加できます。 これは、Windows Communication Foundation (WCF) でサードパーティのタイプ ライブラリを使用する場合など、適切な逆シリアル化のために既知の型を必要とする型を制御しない場合に便利です。

次の構成ファイルは、構成ファイルで既知の種類を指定する方法を示しています。

<configuration>

<system.runtime.serialization>

<dataContractSerializer>

<declaredTypes>

<add type="MyCompany.Library.Shape,

MyAssembly, Version=2.0.0.0, Culture=neutral,

PublicKeyToken=XXXXXX, processorArchitecture=MSIL">

<knownType type="MyCompany.Library.Circle,

MyAssembly, Version=2.0.0.0, Culture=neutral,

PublicKeyToken=XXXXXX, processorArchitecture=MSIL"/>

</add>

</declaredTypes>

</dataContractSerializer>

</system.runtime.serialization>

</configuration>

上記の構成ファイルでは、 MyCompany.Library.Shape というデータ コントラクト型が既知の型として MyCompany.Library.Circle 宣言されています。

こちらも参照ください