Bagikan melalui


Jenis Kontrak Data yang Terkenal

Kelas KnownTypeAttribute memungkinkan Anda menentukan, terlebih dahulu, tipe yang harus disertakan untuk diproses selama deserialisasi. Untuk contoh kerja, lihat contoh Jenis yang Diketahui .

Biasanya, saat meneruskan parameter dan mengembalikan nilai antara klien dan layanan, kedua titik akhir berbagi semua kontrak data data yang akan dikirimkan. Namun, ini tidak terjadi dalam keadaan berikut:

  • Kontrak data yang dikirim didasarkan pada kontrak data yang diharapkan. Untuk informasi selengkapnya, lihat bagian tentang pewarisan dalam Kesepadanan Kontrak Data). Dalam hal ini, data yang dikirimkan tidak memiliki kontrak data yang sama seperti yang diharapkan oleh titik akhir penerimaan.

  • Jenis yang dideklarasikan untuk informasi yang akan ditransmisikan adalah antarmuka, dibandingkan dengan kelas, struktur, atau enumerasi. Oleh karena itu, tidak dapat diketahui terlebih dahulu jenis mana yang mengimplementasikan antarmuka yang benar-benar dikirim dan oleh karena itu, titik akhir penerimaan tidak dapat menentukan terlebih dahulu kontrak data untuk data yang dikirimkan.

  • Jenis yang dinyatakan untuk informasi yang akan ditransmisikan adalah Object. Karena setiap jenis mewarisi dari Object, dan tidak dapat diketahui terlebih dahulu jenis mana yang benar-benar dikirim, titik akhir penerimaan tidak dapat menentukan terlebih dahulu kontrak data untuk data yang dikirimkan. Ini adalah kasus khusus dari item pertama: Setiap kontrak data berasal dari bawaan, yaitu kontrak data kosong yang dihasilkan untuk Object.

  • Beberapa jenis, yang mencakup jenis .NET Framework, memiliki anggota yang berada di salah satu dari tiga kategori sebelumnya. Misalnya, Hashtable menggunakan Object untuk menyimpan objek aktual dalam tabel hash. Saat menserialisasikan jenis ini, sisi penerimaan tidak dapat menentukan terlebih dahulu kontrak data untuk anggota ini.

Kelas KnownTypeAttribute

Ketika data tiba di titik akhir penerimaan, runtime WCF mencoba mendeserialisasi data ke dalam instans jenis runtime bahasa umum (CLR). Jenis yang diinstansiasi untuk deserialisasi dipilih dengan terlebih dahulu memeriksa pesan yang diterima untuk menentukan kontrak data yang sesuai dengan isi pesan tersebut. Mesin deserialisasi kemudian mencoba menemukan jenis CLR yang mengimplementasikan kontrak data yang kompatibel dengan konten pesan. Kumpulan jenis kandidat yang diizinkan mesin deserialisasi selama proses ini disebut sebagai "kumpulan jenis yang diketahui" dari deserializer.

Salah satu cara untuk memberi tahu mesin deserialisasi tentang jenis adalah dengan menggunakan KnownTypeAttribute. Atribut tidak dapat diterapkan ke masing-masing anggota data, hanya untuk seluruh jenis kontrak data. Atribut diterapkan ke jenis luar yang dapat menjadi kelas atau struktur. Dalam penggunaannya yang paling dasar, menerapkan atribut menentukan tipe sebagai "tipe yang dikenal." Ini menyebabkan tipe tersebut menjadi bagian dari kumpulan tipe yang dikenal setiap kali objek dari tipe ini atau objek lain yang dirujuk melalui anggotanya sedang dideserialisasi. Lebih dari satu KnownTypeAttribute atribut dapat diterapkan ke jenis yang sama.

Jenis dan Primitif yang Diketahui

Jenis primitif, serta jenis tertentu yang diperlakukan sebagai primitif (misalnya, DateTime dan XmlElement) selalu "dikenal" dan tidak perlu ditambahkan melalui mekanisme ini. Tetapi, array tipe primitif harus ditambahkan secara eksplisit. Sebagian besar koleksi dianggap setara dengan struktur data array. (Koleksi non-generik dianggap setara dengan array Object). Untuk contoh penggunaan primitif, array primitif, dan koleksi primitif, lihat Contoh 4.

Nota

Tidak seperti jenis primitif lainnya, DateTimeOffset struktur bukan jenis yang diketahui secara default, sehingga harus ditambahkan secara manual ke daftar jenis yang diketahui.

Contoh

Contoh berikut menunjukkan penggunaan kelas KnownTypeAttribute.

Contoh 1

Ada tiga kelas dengan hubungan pewarisan.

[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

Kelas berikut CompanyLogo dapat diserialisasi, tetapi tidak dapat diserialisasi jika anggota ShapeOfLogo diatur ke objek CircleType atau TriangleType, karena mesin pembongkaran tidak mengenali jenis dengan nama kontrak data "Lingkaran" atau "Segitiga."

[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

Cara yang benar untuk menulis CompanyLogo jenis diperlihatkan dalam kode berikut.

[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

Setiap kali tipe CompanyLogo2 eksternal sedang dideserialisasi, mesin deserialisasi tahu tentang CircleType dan TriangleType dan, sehingga dapat menemukan jenis yang cocok untuk kontrak data "Lingkaran" dan "Segitiga".

Contoh 2

Dalam contoh berikut, meskipun baik CustomerTypeA maupun CustomerTypeB memiliki kontrak data Customer, sebuah instans CustomerTypeB dibuat setiap kali PurchaseOrder dideserialisasi, karena hanya CustomerTypeB yang diketahui oleh mesin deserialisasi.

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

Contoh 3

Dalam contoh berikut, Hashtable menyimpan kontennya secara internal sebagai Object. Agar berhasil mendeserialisasi tabel hash, mesin deserialisasi harus mengetahui serangkaian tipe yang mungkin ada di dalamnya. Dalam hal ini, kita tahu terlebih dahulu bahwa hanya Book dan Magazine objek disimpan di Catalog, sehingga ditambahkan menggunakan KnownTypeAttribute atribut .

[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

Contoh 4

Dalam contoh berikut, kontrak data menyimpan angka dan operasi untuk dilakukan pada angka tersebut. Anggota Numbers data dapat berupa bilangan bulat, array bilangan bulat, atau List<T> yang berisi bilangan bulat.

Perhatian

Ini hanya akan berfungsi di sisi klien jika SVCUTIL.EXE digunakan untuk menghasilkan proksi WCF. SVCUTIL.EXE mengambil metadata dari layanan termasuk tipe yang sudah dikenal. Tanpa informasi ini, klien tidak akan dapat mendeserialisasi tipe data.

[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

Ini adalah kode aplikasi.

// 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

Jenis, Pewarisan, dan Antarmuka yang Diketahui

Ketika jenis yang diketahui dikaitkan dengan jenis tertentu menggunakan KnownTypeAttribute atribut , jenis yang diketahui juga dikaitkan dengan semua jenis turunan dari jenis tersebut. Misalnya, lihat kode berikut.

[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

Kelas DoubleDrawing tidak memerlukan KnownTypeAttribute atribut untuk digunakan Square dan Circle di AdditionalShape bidang , karena kelas dasar (Drawing) sudah menerapkan atribut ini.

Jenis yang diketahui hanya dapat dikaitkan dengan kelas dan struktur, bukan antarmuka.

Jenis yang Diketahui Menggunakan Metode Generik Terbuka

Mungkin perlu menambahkan jenis generik sebagai jenis yang diketahui. Namun, jenis generik terbuka tidak dapat diteruskan sebagai parameter ke KnownTypeAttribute atribut .

Masalah ini dapat diselesaikan dengan menggunakan mekanisme alternatif: Tulis metode yang mengembalikan daftar jenis untuk ditambahkan ke koleksi jenis yang diketahui. Nama metode kemudian ditentukan sebagai argumen string ke KnownTypeAttribute atribut karena beberapa batasan.

Metode harus ada pada jenis yang KnownTypeAttribute atributnya diterapkan, harus statis, tidak boleh menerima parameter, dan harus mengembalikan objek yang dapat ditetapkan ke IEnumerable dari Type.

Anda tidak dapat menggabungkan atribut KnownTypeAttribute dengan nama metode dan atribut KnownTypeAttribute dengan tipe konkret pada tipe yang sama. Selain itu, Anda tidak dapat menerapkan lebih dari satu KnownTypeAttribute dengan nama metode yang sama ke tipe yang sama.

Lihat kelas berikut.

[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

Bidang theDrawing berisi instans kelas generik ColorDrawing dan BlackAndWhiteDrawing, yang keduanya merupakan turunan dari kelas generik Drawing. Biasanya, keduanya harus ditambahkan ke jenis yang diketahui, tetapi berikut ini bukan sintaks yang valid untuk atribut.

// 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)))>  

Dengan demikian, metode harus dibuat untuk mengembalikan jenis ini. Cara yang benar untuk menulis jenis ini, kemudian, ditunjukkan dalam kode berikut.

[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

Cara Tambahan untuk Menambahkan Jenis yang Diketahui

Selain itu, jenis yang diketahui dapat ditambahkan melalui file konfigurasi. Ini berguna ketika Anda tidak mengendalikan tipe yang memerlukan tipe yang dikenal untuk deserialisasi yang tepat, seperti saat menggunakan pustaka tipe pihak ketiga dengan Windows Communication Foundation (WCF).

File konfigurasi berikut menunjukkan cara menentukan jenis yang diketahui dalam file konfigurasi.

<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>

Dalam file konfigurasi sebelumnya, jenis kontrak data yang disebut MyCompany.Library.Shape dinyatakan memiliki MyCompany.Library.Circle sebagai jenis yang diketahui.

Lihat juga