Adatszerződés ismert típusai
Az KnownTypeAttribute osztály segítségével előre megadhatja azokat a típusokat, amelyeket figyelembe kell venni a deszerializálás során. Egy működő példáért tekintse meg az Ismert típusok példát.
Általában, amikor paramétereket ad át, és értékeket ad vissza egy ügyfél és egy szolgáltatás között, mindkét végpont megosztja a továbbítandó adatok összes adatszerződését. A következő körülmények között azonban nem ez a helyzet:
Az elküldött adatszerződés a várt adatszerződésből származik. További információkért tekintse meg az adatszerződések egyenértékűségének örökléséről szóló szakaszt. Ebben az esetben a továbbított adatok nem rendelkeznek a fogadó végpont által elvárt adatszerződéssel.
Az továbbítandó információk deklarált típusa egy interfész, szemben az osztályokkal, szerkezetekkel vagy enumerálással. Ezért nem lehet előre tudni, hogy melyik típus implementálja az interfészt, ezért a fogadó végpont nem tudja előre meghatározni a továbbított adatokra vonatkozó adatszerződést.
A továbbítandó információk deklarált típusa a Objectkövetkező. Mivel minden típus öröklődik, Objectés nem lehet előre tudni, hogy melyik típust küldi el a rendszer, a fogadó végpont nem tudja előre meghatározni a továbbított adatokra vonatkozó adatszerződést. Ez az első elem különleges esete: Minden adatszerződés az alapértelmezett, egy üres adatszerződésből származik, amely a következőhöz Objectjön létre: .
Egyes típusok, amelyek .NET-keretrendszer típusokat is tartalmaznak, az előző három kategóriában szereplő tagokkal rendelkeznek. Például Hashtable a tényleges objektumok tárolására használható Object a kivonattáblában. Az ilyen típusok szerializálásakor a fogadó fél nem tudja előre meghatározni a tagokra vonatkozó adatszerződést.
A KnownTypeAttribute osztály
Amikor az adatok fogadó végpontra érkeznek, a WCF-futtatókörnyezet megpróbálja deszerializálni az adatokat egy általános nyelvi futtatókörnyezet (CLR) típusú példányba. A deszerializáláshoz példányosított típust úgy választja ki, hogy először megvizsgálja a bejövő üzenetet annak az adatszerződésnek a meghatározásához, amelynek az üzenet tartalma megfelel. A deszerializálási motor ezután megpróbál megkeresni egy OLYAN CLR-típust, amely az üzenet tartalmával kompatibilis adatszerződést valósít meg. A deszerializálási motor által a folyamat során lehetővé tetsző típusokat a deszerializáló "ismert típusok" készletének nevezzük.
Az egyik módja annak, hogy a deszerializálási motor tudassa egy típussal a KnownTypeAttribute. Az attribútum nem alkalmazható az egyes adattagokra, csak teljes adatszerződés-típusokra. Az attribútum egy külső típusra lesz alkalmazva, amely lehet osztály vagy struktúra. A legalapvetőbb használatban az attribútum alkalmazása egy típust "ismert típusként" határoz meg. Ez azt eredményezi, hogy az ismert típus az ismert típusok csoportjának része lesz, amikor a külső típusú objektumot vagy a tagjain keresztül hivatkozott objektumot deszerializálják. KnownTypeAttribute Több attribútum is alkalmazható ugyanahhoz a típushoz.
Ismert típusok és primitívek
A primitív típusok, valamint a primitívként kezelt egyes típusok (például és XmlElement) mindig "ismertek", DateTime és soha nem kell ezen a mechanizmuson keresztül hozzáadni. A primitív típusú tömböket azonban explicit módon kell hozzáadni. A gyűjtemények többsége tömböknek felel meg. (A nem általános gyűjtemények egyenértékűek a tömbökével Object. Példa a primitívek, primitív tömbök és primitív gyűjtemények használatára: 4. példa.
Feljegyzés
A többi primitív típustól eltérően a DateTimeOffset struktúra alapértelmezés szerint nem ismert típus, ezért manuálisan kell hozzáadni az ismert típusok listájához.
Példák
Az alábbi példák a használatban lévő osztályt KnownTypeAttribute mutatják be.
1. példa
Három osztály van öröklési kapcsolatban.
[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
A következő CompanyLogo
osztály szerializálható, de nem deszerializálható, ha a ShapeOfLogo
tag egy vagy egy TriangleType
objektumra CircleType
van állítva, mert a deszerializálási motor nem ismer fel olyan típusokat, amelyek "Circle" vagy "Triangle" nevű adatszerződéssel rendelkeznek.
[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
A típus megírásának CompanyLogo
helyes módja az alábbi kódban látható.
[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
Amikor a külső típust CompanyLogo2
deszerializálják, a deszerializálási motor tud róla CircleType
, és TriangleType
ezért képes megtalálni a "Circle" és a "Triangle" adatszerződések megfelelő típusait.
2. példa
A következő példában annak ellenére, hogy mind a kettő CustomerTypeA
CustomerTypeB
, mind az Customer
adatszerződéssel rendelkezik, a rendszer akkor hoz létre példányt CustomerTypeB
PurchaseOrder
, amikor deszerializál egy példányt, mert csak CustomerTypeB
a deszerializálási motor ismeri.
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. példa
A következő példában a Hashtable tartalom belsőleg a következőképpen van tárolva Object: . A kivonattáblák sikeres deszerializálásához a deszerializálási motornak ismernie kell az ott előforduló lehetséges típusokat. Ebben az esetben előre tudjuk, hogy csak Book
és Magazine
az objektumok vannak tárolva a Catalog
, így azokat az KnownTypeAttribute attribútum használatával adjuk hozzá.
[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. példa
Az alábbi példában egy adatszerződés egy számot és egy műveletet tárol a számon. Az Numbers
adattag lehet egész szám, egész számok tömbje vagy List<T> egész számokat tartalmazó tömb.
Figyelemfelhívás
Ez csak akkor működik az ügyféloldalon, ha SVCUTIL.EXE használ egy WCF-proxy létrehozásához. SVCUTIL.EXE lekéri a metaadatokat a szolgáltatásból, beleértve az ismert típusokat is. Ezen információk nélkül az ügyfél nem tudja deszerializálni a típusokat.
[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
Ez az alkalmazás kódja.
// 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
Ismert típusok, öröklés és interfészek
Ha egy ismert típus egy adott típushoz van társítva az KnownTypeAttribute
attribútum használatával, az ismert típus az adott típus összes származtatott típusához is társítva lesz. Lásd például a következő kódot.
[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
Az DoubleDrawing
osztálynak nincs szüksége az KnownTypeAttribute
attribútum használatára és Circle
a AdditionalShape
mezőben való használatáraSquare
, mert az alaposztály (Drawing
) már alkalmazza ezeket az attribútumokat.
Az ismert típusok csak osztályokhoz és struktúrákhoz társíthatók, interfészekhez nem.
Ismert típusok open generic metódusokkal
Előfordulhat, hogy általános típust kell hozzáadni ismert típusként. Nyitott általános típus azonban nem adható át paraméterként az KnownTypeAttribute
attribútumnak.
Ez a probléma egy alternatív mechanizmussal oldható meg: Írjon egy metódust, amely visszaadja az ismert típusok gyűjteményéhez hozzáadni kívánt típusok listáját. A metódus neve ezután bizonyos korlátozások miatt sztringargumentumként lesz megadva az KnownTypeAttribute
attribútumhoz.
A metódusnak azon a típuson kell léteznie, amelyre az KnownTypeAttribute
attribútumot alkalmazza, statikusnak kell lennie, nem szabad paramétereket elfogadnia, és egy olyan objektumot kell visszaadnia IEnumerableType, amelyhez hozzárendelhető.
Az attribútum nem kombinálható KnownTypeAttribute
metódusnévvel és KnownTypeAttribute
attribútumokkal az azonos típusú tényleges típusokkal. Emellett nem alkalmazhat egynél KnownTypeAttribute
több metódusnevet ugyanarra a típusra.
Lásd az alábbi osztályt.
[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
A theDrawing
mező egy általános osztály és egy általános osztály ColorDrawing
BlackAndWhiteDrawing
példányait tartalmazza, amelyek mindegyike egy általános osztálytól Drawing
öröklődik. Általában mindkettőt hozzá kell adni az ismert típusokhoz, de az alábbiak nem érvényesek az attribútumok szintaxisára.
// 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)))>
Ezért létre kell hozni egy metódust, amely visszaadja ezeket a típusokat. A típus megírásának helyes módja az alábbi kódban jelenik meg.
[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
Ismert típusok hozzáadásának további módjai
Emellett az ismert típusok egy konfigurációs fájlon keresztül is hozzáadhatók. Ez akkor hasznos, ha nem szabályozza azt a típust, amely a megfelelő deszerializáláshoz ismert típusokat igényel, például ha külső típuskódtárakat használ a Windows Communication Foundation (WCF) használatával.
Az alábbi konfigurációs fájl bemutatja, hogyan adhat meg egy ismert típust egy konfigurációs fájlban.
<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>
Az előző konfigurációs fájlban a hívott MyCompany.Library.Shape
adatszerződés típusa ismert típusként van deklarálva MyCompany.Library.Circle
.