Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Класс KnownTypeAttribute позволяет заранее указать типы, которые следует включить для рассмотрения во время десериализации. Пример работы см. в примере известных типов .
Как правило, при передаче параметров и возвращаемых значений между клиентом и службой обе конечные точки совместно используют все контракты данных, передаваемые. Однако это не так в следующих обстоятельствах:
Контракт отправленных данных является производным от ожидаемого контракта данных. Дополнительную информацию см. в разделе о наследовании в Эквивалентности контрактов данных. В этом случае передаваемые данные не имеют того же контракта данных, что и ожидалось принимающей конечной точкой.
Объявленный тип для передачи информации — это интерфейс, а не класс, структура или перечисление. Поэтому невозможно заранее определить, какой тип, реализующий интерфейс, фактически отправляется, и поэтому принимающая конечная точка не может заранее установить договор данных для передаваемых данных.
Объявленный тип информации, которая будет передаваться, — это Object. Так как каждый тип наследуется от Object, и он не может быть известен заранее, какой тип фактически отправляется, получающая конечная точка не может заранее определить контракт данных для передаваемых данных. Это особый случай первого элемента: все контракты данных происходят от стандартного пустого контракта данных, созданного для Object.
Некоторые типы, включающие типы .NET Framework, имеют члены, которые находятся в одной из предыдущих трех категорий. Например, Hashtable используется Object для хранения фактических объектов в хэш-таблице. При сериализации этих типов принимающая сторона не может заранее определить контракт данных для этих элементов.
Класс KnownTypeAttribute
Когда данные поступают в получающую конечную точку, среда выполнения WCF пытается десериализировать данные в экземпляр типа CLR. Тип, созданный для десериализации, выбирается путем проверки входящего сообщения, чтобы определить контракт данных, к которому соответствует содержимое сообщения. Затем механизм десериализации пытается найти тип CLR, заключающий контракт данных, совместимый с содержимым сообщения. Набор типов кандидатов, которые подсистема десериализации разрешает использовать во время этого процесса, называется набором «известных типов» десериализатора.
Один из способов сообщить обработчику десериализации о типе — использование KnownTypeAttribute. Атрибут нельзя применять к отдельным элементам данных, только ко всему типу контракта данных. Атрибут применяется к внешнему типу , который может быть классом или структурой. При наиболее простом применении атрибут указывает тип как «известный тип». Это приводит к тому, что известный тип становится частью множества известных типов всякий раз, когда объект внешнего типа или любой объект, на который ссылаются его члены, подвергается десериализации. К одному KnownTypeAttribute типу можно применить несколько атрибутов.
Известные типы и примитивы
Примитивные типы, а также некоторые типы, которые рассматриваются как примитивы (например, DateTime и XmlElement) всегда "известны" и никогда не должны добавляться с помощью этого механизма. Однако массивы примитивных типов необходимо добавить явным образом. Большинство коллекций считаются эквивалентными массивам. (Не универсальные коллекции считаются эквивалентными массивам Object). Пример использования примитивов, примитивных массивов и примитивных коллекций см. в примере 4.
Замечание
В отличие от других примитивных типов, DateTimeOffset структура не является известным типом по умолчанию, поэтому она должна быть добавлена вручную в список известных типов.
Примеры
В следующих примерах показан используемый KnownTypeAttribute класс.
Пример 1
Существуют три класса, связанные между собой отношением наследования.
[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
можно сериализовать, но не удастся десериализовать, если член ShapeOfLogo
задается либо объектом CircleType
либо TriangleType
, так как механизм десериализации не распознает какие-либо типы с именами контрактов данных "Circle" или "Triangle."
[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
десериализуется, подсистема десериализации знает о CircleType
и TriangleType
, следовательно, может находить соответствующие типы для контрактов данных "Circle" и "Треугольник".
Пример 2
В следующем примере, даже несмотря на то, что и CustomerTypeA
, и CustomerTypeB
имеют контракт данных 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
и Circle
в поле AdditionalShape
, так как базовый класс (Drawing
) уже применяет эти атрибуты.
Известные типы могут быть связаны только с классами и структурами, а не с интерфейсами.
Известные типы, используя открытые универсальные методы
Может потребоваться добавить универсальный тип как известный тип. Однако открытый универсальный тип нельзя передать в качестве параметра атрибуту 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
как известного типа.