既知のデータ コントラクト型
KnownTypeAttribute クラスを使用すると、逆シリアル化において考慮する必要のある型を事前に指定できます。実施例については、「既知の型」を参照してください。
通常は、クライアントとサービス間でパラメーターを渡したり、値を返したりするときに、転送するデータのデータ コントラクトのすべてが両方のエンドポイントで共有されます。ただし、次の場合はこれが該当しません。
送信データ コントラクトが、予想データ コントラクトから派生する場合 (詳細については、次のトピックを参照してください。「データ コントラクトの等価性」の継承に関するセクションを参照してください)。この場合、送信されるデータには、受信エンドポイントで予想しているデータ コントラクトが含まれません。
クラス、構造体、または列挙とは対照的に、送信される情報の宣言型がインターフェイスである場合。したがって、インターフェイスを実装するどの型が実際に送信されるかを事前に知ることができないため、受信エンドポイントでは送信されるデータのデータ コントラクトを事前に確認することができません。
送信される情報の宣言型が Object である場合。すべての型が Object から継承され、どの型が実際に送信されるかを事前に知ることができないため、受信エンドポイントでは送信されるデータのデータ コントラクトを事前に確認することができません。これは最初の項目についての特殊なケースです。すべてのデータ コントラクトは既定で、Object に対して生成された空のデータ コントラクトから派生します。
.NET Framework 型を含む一部の型に、前述した 3 つのカテゴリの 1 つに分類されるメンバーが含まれる場合。たとえば、Hashtable は Object を使用して、ハッシュ テーブルに実際のオブジェクトを保存します。これらの型をシリアル化する場合、受信側ではこれらのメンバーのデータ コントラクトを事前に確認することができません。
KnownTypeAttribute クラス
データが受信エンドポイントに到着すると、WCF ランタイムはデータを共通言語ランタイム (CLR: Common Language Runtime) 型のインスタンスに逆シリアル化しようとします。逆シリアル化するためにインスタンス化される型は、まず受信メッセージを調べてメッセージの内容が従うデータ コントラクトを特定することで選択されます。次に逆シリアル化エンジンは、メッセージの内容と互換性のあるデータ コントラクトを実装する CLR 型を探します。逆シリアル化エンジンによってこの処理中に逆シリアル化の候補の型として許可される一連の型は、逆シリアル化の "既知の型" のセットと呼ばれます。
逆シリアル化エンジンに型の情報を知らせる方法として、KnownTypeAttribute を使用する方法があります。この属性は、データ コントラクト型全体に適用できるだけで、個々のデータ メンバーには適用できません。この属性は、クラスまたは構造体にすることが可能な外部型に適用されます。最も簡単な使用方法は、属性を適用するときに "既知の型" として型を指定することです。これによって、外部型のオブジェクトまたはメンバーを通して参照される任意のオブジェクトが逆シリアル化されるときに、必ず、その既知の型が既知の型のセットに追加されます。複数の KnownTypeAttribute 属性を同じ型に適用することができます。
既知の型とプリミティブ
プリミティブとして扱われる特定の型 (DateTime、XmlElement など) と同様に、プリミティブ型は、常に "既知" であり、このメカニズムを通して追加する必要がありません。ただし、プリミティブ型の配列は明示的に追加する必要があります。ほとんどのコレクションは、配列と同等に扱われます。(非ジェネリック コレクションは、Object の配列と同等に扱われます)。プリミティブ、プリミティブ配列、およびプリミティブ コレクションの使用例については、例 4 を参照してください。
注 : |
---|
他のプリミティブ型とは異なり、DateTimeOffset 構造は、既定では既知の型ではないため、既知の型のリストに手動で追加する必要があります。 |
例
KnownTypeAttribute クラスの使用例を次に示します。
例 1
継承関係にある 3 つのクラスがあります。
<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
[DataContract]
public class Shape { }
[DataContract(Name = "Circle")]
public class CircleType : Shape { }
[DataContract(Name = "Triangle")]
public class TriangleType : Shape { }
次の CompanyLogo
クラスはシリアル化できますが、ShapeOfLogo
メンバーが CircleType
オブジェクトと TriangleType
オブジェクトのどちらかに設定されている場合は、逆シリアル化エンジンが "Circle" や "Triangle" という名前のデータ コントラクト型を認識しないため、逆シリアル化することはできません。
<DataContract()> _
Public Class CompanyLogo
<DataMember()> _
Private ShapeOfLogo As Shape
<DataMember()> _
Private ColorOfLogo As Integer
End Class
[DataContract]
public class CompanyLogo
{
[DataMember]
private Shape ShapeOfLogo;
[DataMember]
private int ColorOfLogo;
}
CompanyLogo
型の正しいコードの記述方法は次のとおりです。
<DataContract(), KnownType(GetType(CircleType)), KnownType(GetType(TriangleType))> _
Public Class CompanyLogo2
<DataMember()> _
Private ShapeOfLogo As Shape
<DataMember()> _
Private ColorOfLogo As Integer
End Class
[DataContract]
[KnownType(typeof(CircleType))]
[KnownType(typeof(TriangleType))]
public class CompanyLogo2
{
[DataMember]
private Shape ShapeOfLogo;
[DataMember]
private int ColorOfLogo;
}
外部型の CompanyLogo2
が逆シリアル化されるときは、必ず、逆シリアル化エンジンが CircleType
と TriangleType
を認識するため、"Circle" データ コントラクトおよび "Triangle" データ コントラクトと一致する型を検出できます。
例 2
次の例では、CustomerTypeA
と CustomerTypeB
の両方が Customer
データ コントラクトを持っている場合でも、逆シリアル化エンジンは CustomerTypeB
だけを認識するため、PurchaseOrder
が逆シリアル化されるときは必ず CustomerTypeB
のインスタンスが作成されます。
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
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;
}
例 3
次の例では、Hashtable が Object として内部的にその内容を保存します。ハッシュ テーブルを正常に逆シリアル化するには、逆シリアル化エンジンがそのテーブルに現れる可能性のある型のセットを認識している必要があります。この場合は、Book
オブジェクトと Magazine
オブジェクトだけが Catalog
に保存されているため、この 2 つが KnownTypeAttribute 属性を使用して追加されることが事前にわかっています。
<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
[DataContract]
public class Book { }
[DataContract]
public class Magazine { }
[DataContract]
[KnownType(typeof(Book))]
[KnownType(typeof(Magazine))]
public class LibraryCatalog
{
[DataMember]
System.Collections.Hashtable theCatalog;
}
例 4
次の例では、データ コントラクトには数値と、その数値に基づいて実行する演算が含まれています。Numbers
データ メンバーは、整数、整数の配列、または整数を含む List にすることができます。
注意 : |
---|
この例は、WCF プロキシを生成するために SVCUTIL.EXE が使用されている場合にのみ、クライアント側で機能します。SVCUTIL.EXE は、サービスからメタデータ (任意の既知の型を含む) を取得します。この情報がない場合、クライアントは型を逆シリアル化できません。 |
<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
[DataContract]
[KnownType(typeof(int[]))]
public class MathOperationData
{
private object numberValue;
[DataMember]
public object Numbers
{
get { return numberValue; }
set { numberValue = value; }
}
//[DataMember]
//public Operation Operation;
}
アプリケーション コードは次のとおりです。
' 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
// 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;
}
既知の型、継承、およびインターフェイス
KnownTypeAttribute 属性を使用して既知の型を特定の型に関連付けると、その既知の型がその特定の型から派生したすべての型に関連付けられます。たとえば、次のコードを参照してください。
<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
[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;
}
DoubleDrawing
クラスは、AdditionalShape
フィールドで Square
と Circle
を使用するために KnownTypeAttribute 属性を必要としません。これは、既に基本クラス (Drawing
) にこれらの属性が適用されているためです。
既知の型は、クラスと構造体にのみ関連付けることができます。インターフェイスに関連付けることはできません。
オープン ジェネリック メソッドを使用する既知の型
既知の型としてジェネリック型の追加が必要な場合があります。ただし、オープン ジェネリック型をパラメーターとして KnownTypeAttribute 属性に渡すことはできません。
この問題は、型の一覧を返すメソッドを作成して既知の型のコレクションに追加するという代替機構を使用することで解決できます。次に、いくつかの制限事項があるため、このメソッドの名前を KnownTypeAttribute 属性への文字列引数として指定します。
このメソッドは、KnownTypeAttribute 属性を適用する型上に存在し、静的で、パラメーターを必要とせず、Type の IEnumerable に代入可能なオブジェクトを返す必要があります。
同じ型上で、メソッド名を持つ KnownTypeAttribute 属性と実際の型を持つ KnownTypeAttribute 属性を組み合わせることはできません。また、メソッド名が同じ複数の KnownTypeAttribute を同じ型に適用することはできません。
次のクラスを参照してください。
<DataContract()> _
Public Class DrawingRecord(Of T)
<DataMember()> _
Private theData As T
<DataMember()> _
Private theDrawing As GenericDrawing(Of T)
End Class
[DataContract]
public class DrawingRecord<T>
{
[DataMember]
private T theData;
[DataMember]
private GenericDrawing<T> theDrawing;
}
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(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
[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;
}
}
既知の型を追加するその他の方法
型を ReadOnlyCollection に追加し、DataContractSerializer の KnownTypes プロパティを使用してアクセスできます。
また、既知の型は、構成ファイルを使用して追加することもできます。この方法は、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
を既知の型として持つと宣言されています。
参照
処理手順
リファレンス
KnownTypeAttribute
Hashtable
Object
DataContractSerializer
KnownTypes