Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Суррогат контракта данных — это расширенная функция, основанная на модели контракта данных. Эта функция предназначена для настройки и подстановки типов в ситуациях, когда пользователи хотят изменить сериализацию, десериализацию или проекцию типа в метаданные. Некоторые сценарии, в которых может использоваться суррогат, это когда контракт данных не указан для типа, поля и свойства не отмечены атрибутом DataMemberAttribute, или пользователи хотят динамически создавать варианты схемы.
Сериализация и десериализация выполняются с помощью суррогата контракта данных, когда используется DataContractSerializer для преобразования из .NET Framework в подходящий формат, например XML. Суррогат контракта данных можно также использовать для изменения метаданных, экспортируемых для типов, при создании представлений метаданных, таких как XML-схема (XSD). При импорте код создается из метаданных и суррогат можно использовать в этом случае для настройки созданного кода.
Как работает суррогат
Суррогат работает путем сопоставления одного типа (исходного типа) с другим типом (суррогатным типом). В следующем примере показан исходный тип Inventory и новый суррогатный InventorySurrogated тип. Тип Inventory не сериализуем, но InventorySurrogated тип:
public class Inventory
{
public int pencils;
public int pens;
public int paper;
}
Так как контракт данных не определен для этого класса, преобразуйте класс в суррогатный класс с контрактом данных. Суррогатный класс показан в следующем примере:
[DataContract(Name = "Inventory")]
public class InventorySurrogated
{
[DataMember]
public int numpencils;
[DataMember]
public int numpaper;
[DataMember]
private int numpens;
public int pens
{
get { return numpens; }
set { numpens = value; }
}
}
Реализация интерфейса IDataContractSurrogate
Чтобы использовать суррогатный контракт данных, реализуйте интерфейс IDataContractSurrogate.
Ниже приведен обзор каждого метода IDataContractSurrogate с возможной реализацией.
ПолучитьТипКонтрактаДанных
Метод GetDataContractType сопоставляет один тип с другим. Этот метод необходим для сериализации, десериализации, импорта и экспорта.
Первая задача определяет, какие типы будут сопоставлены с другими типами. Рассмотрим пример.
public Type GetDataContractType(Type type)
{
Console.WriteLine("GetDataContractType");
if (typeof(Inventory).IsAssignableFrom(type))
{
return typeof(InventorySurrogated);
}
return type;
}
При сериализации сопоставление, возвращаемое этим методом, впоследствии используется для преобразования исходного экземпляра в суррогатный экземпляр путем вызова GetObjectToSerialize метода.
При десериализации сопоставление, возвращаемое этим методом, используется сериализатором для десериализации в экземпляр суррогатного типа. Впоследствии он вызывает GetDeserializedObject, чтобы преобразовать суррогатный экземпляр в экземпляр исходного типа.
При экспорте суррогатный тип, возвращаемый этим методом, анализируется, чтобы определить контракт данных, который будет использоваться для создания метаданных.
При импорте исходный тип изменяется на суррогатный тип, который отражается для получения контракта данных для использования в таких целях, как поддержка ссылок.
Параметр Type — это тип объекта, сериализованного, десериализуемого, импортированного или экспортированного. Метод GetDataContractType должен возвращать входной тип, если суррогат не обрабатывает тип. В противном случае верните соответствующий суррогатный тип. Если существуют несколько суррогатных типов, в этом методе можно определить многочисленные сопоставления.
Метод GetDataContractType не вызывается для встроенных примитивов контрактов данных, таких как Int32 или String. Для других типов, таких как массивы, определяемые пользователем типы и другие структуры данных, этот метод будет вызываться для каждого типа.
В предыдущем примере метод проверяет, являются ли параметры type и Inventory сопоставимыми. Если да, метод сопоставляет его с InventorySurrogated. При каждом вызове сериализации, десериализации, импорта схемы или схемы экспорта эта функция вызывается сначала для определения сопоставления между типами.
Метод GetObjectToSerialize
Метод GetObjectToSerialize преобразует исходный экземпляр типа в суррогатный экземпляр типа. Метод необходим для сериализации.
Следующим шагом является определение способа, с помощью которого физические данные будут сопоставлены от исходного экземпляра к суррогатному, путем реализации метода GetObjectToSerialize. Рассмотрим пример.
public object GetObjectToSerialize(object obj, Type targetType)
{
Console.WriteLine("GetObjectToSerialize");
if (obj is Inventory)
{
InventorySurrogated isur = new InventorySurrogated();
isur.numpaper = ((Inventory)obj).paper;
isur.numpencils = ((Inventory)obj).pencils;
isur.pens = ((Inventory)obj).pens;
return isur;
}
return obj;
}
Метод GetObjectToSerialize вызывается при сериализации объекта. Этот метод передает данные из исходного типа в поля суррогатного типа. Поля можно напрямую сопоставить с суррогатными полями или манипуляции с исходными данными могут храниться в суррогатных полях. Некоторые возможные варианты использования включают: непосредственное сопоставление полей, выполнение операций с данными, хранящихся в суррогатных полях, или хранение XML исходного типа в суррогатном поле.
Параметр targetType относится к объявленному типу элемента. Этот параметр является суррогатным типом, возвращаемым методом GetDataContractType . Сериализатор не обеспечивает назначение возвращаемого объекта этому типу. Параметр obj является объектом для сериализации и преобразуется в его суррогат при необходимости. Этот метод должен возвращать входной объект, если суррогатный объект не обрабатывает объект. В противном случае будет возвращен новый суррогатный объект. Суррогат не вызывается, если объект имеет значение NULL. В этом методе могут быть определены многочисленные суррогатные сопоставления для разных экземпляров.
При создании DataContractSerializerможно указать ему сохранить ссылки на объекты. (Дополнительные сведения см. в разделе Сериализация и десериализация.) Это делается путем установки параметра preserveObjectReferences в его конструкторе на true. В этом случае суррогат вызывается только один раз для объекта, так как все последующие сериализации просто записывают ссылку в поток. Если preserveObjectReferences установлено на false, дублёр вызывается при каждом обнаружении экземпляра.
Если тип сериализованного экземпляра отличается от объявленного типа, сведения о типе записываются в поток, например xsi:type , чтобы разрешить десериализацию экземпляра в другом конце. Этот процесс возникает, является ли объект суррогатным или нет.
В приведенном выше примере данные экземпляра Inventory преобразуются в данные InventorySurrogated. Он проверяет тип объекта и выполняет необходимые манипуляции для преобразования в суррогатный тип. В этом случае поля Inventory класса копируются непосредственно в InventorySurrogated поля класса.
Метод GetDeserializedObject
Метод GetDeserializedObject преобразует суррогатный экземпляр типа в исходный экземпляр типа. Он необходим для десериализации.
Следующая задача — определить способ сопоставления физических данных из суррогатного экземпляра с исходным. Рассмотрим пример.
public object GetDeserializedObject(object obj, Type targetType)
{
Console.WriteLine("GetDeserializedObject");
if (obj is InventorySurrogated)
{
Inventory invent = new Inventory();
invent.pens = ((InventorySurrogated)obj).pens;
invent.pencils = ((InventorySurrogated)obj).numpencils;
invent.paper = ((InventorySurrogated)obj).numpaper;
return invent;
}
return obj;
}
Этот метод вызывается только во время десериализации объекта. Предоставляет обратное отображение данных для десериализации из суррогатного типа обратно к исходному типу. Подобно методу GetObjectToSerialize, некоторые возможные способы использования могут включать непосредственный обмен данными полей, выполнение операций с данными и хранение XML-данных. При десериализации вы не всегда можете получить точные значения данных из исходного из-за манипуляций в преобразовании данных.
Параметр targetType относится к объявленному типу элемента. Этот параметр является суррогатным типом, возвращаемым методом GetDataContractType . Параметр obj ссылается на объект, который был десериализирован. Объект можно преобразовать обратно в исходный тип, если он суррогат. Этот метод возвращает входной объект, если суррогат не обрабатывает объект. В противном случае десериализированный объект будет возвращен после завершения его преобразования. Если существуют несколько суррогатных типов, можно предоставить преобразование данных из суррогата в первичный тип для каждого, указав каждый тип и его преобразование.
При возврате объекта внутренние таблицы объектов обновляются с помощью объекта, возвращаемого этим суррогатом. Все последующие ссылки на экземпляр получат суррогатный экземпляр из таблиц объектов.
В предыдущем примере объекты типа InventorySurrogated преобразуются обратно в исходный тип Inventory. В этом случае данные передаются обратно из InventorySurrogated в соответствующие поля Inventory. Так как нет манипуляций данными, каждый из полей-членов будет содержать те же значения, что и перед сериализацией.
Метод GetCustomDataToExport
При экспорте схемы GetCustomDataToExport метод необязателен. Он используется для вставки дополнительных данных или подсказок в экспортируемую схему. Дополнительные данные можно вставить на уровне члена или на уровне типа. Рассмотрим пример.
public object GetCustomDataToExport(System.Reflection.MemberInfo memberInfo, Type dataContractType)
{
Console.WriteLine("GetCustomDataToExport(Member)");
System.Reflection.FieldInfo fieldInfo = (System.Reflection.FieldInfo)memberInfo;
if (fieldInfo.IsPublic)
{
return "public";
}
else
{
return "private";
}
}
Этот метод (с двумя перегрузками) обеспечивает включение дополнительных сведений в метаданные на уровне элемента или типа. Можно включить указания о том, является ли элемент общедоступным или частным, и комментарии, которые будут сохранены во время экспорта и импорта схемы. Такая информация будет потеряна без этого метода. Этот метод не вызывает вставку или удаление элементов или типов, а добавляет дополнительные данные в схемы на любом из этих уровней.
Метод перегружен и может принимать (Typeclrtypeпараметр) или MemberInfo (memberInfoпараметр). Второй параметр всегда Type (dataContractType параметр). Этот метод вызывается для каждого элемента и типа суррогатного dataContractType типа.
Каждая из этих перегрузок должна возвращать либо null, либо сериализуемый объект. Объект, отличный от NULL, сериализуется в виде заметки в экспортируемой схеме. Для перегрузки Type каждый тип, экспортируемый в схему, передается в этот метод в качестве первого параметра вместе с суррогатным типом в качестве параметра dataContractType. Для перегрузки MemberInfo каждый элемент, экспортируемый в схему, отправляет свои данные в качестве параметра memberInfo со суррогатным типом во втором параметре.
Метод GetCustomDataToExport (для Type, Type)
Метод IDataContractSurrogate.GetCustomDataToExport(Type, Type) вызывается во время экспорта схемы для каждого определения типа. Метод добавляет сведения к типам в схеме при экспорте. Каждый тип отправляется в этот метод для определения наличия дополнительных данных, которые необходимо включить в схему.
Метод GetCustomDataToExport (MemberInfo, Type)
IDataContractSurrogate.GetCustomDataToExport(MemberInfo, Type) вызывается во время экспорта для каждого элемента в экспортируемых типах. Эта функция позволяет настраивать любые комментарии для элементов, которые будут включены в схему при экспорте. Сведения для каждого члена класса отправляются этому методу, чтобы проверить, нужно ли добавлять дополнительные данные в схему.
Приведенный выше пример осуществляет поиск в dataContractType для каждого элемента суррогата. Затем он возвращает соответствующий модификатор доступа для каждого поля. Без этой настройки значение по умолчанию для модификаторов доступа является общедоступным. Таким образом, все члены будут определены как общедоступные в коде, созданном с помощью экспортируемой схемы независимо от того, какие ограничения доступа являются фактическими. Если эта реализация не используется, член numpens будет общедоступным в экспортируемой схеме, даже если он был определен в суррогате как закрытый. С помощью этого метода в экспортируемой схеме модификатор доступа можно создать как закрытый.
Метод GetReferencedTypeOnImport
Этот метод сопоставляет Type суррогат с исходным типом. Этот метод является необязательным для импорта схемы.
При создании суррогата, который импортирует схему и генерирует для неё код, следующая задача — установить соответствие типа экземпляра суррогата его исходному типу.
Если созданный код должен ссылаться на существующий тип пользователя, это делается путем реализации GetReferencedTypeOnImport метода.
При импорте схемы этот метод вызывается для каждого объявления типа для сопоставления суррогатного контракта данных с типом. Строковые параметры typeName и typeNamespace определяют имя и пространство имен суррогатного типа. Возвращаемое значение GetReferencedTypeOnImport используется для определения необходимости создания нового типа. Этот метод должен возвращать допустимый тип или null. Для допустимых типов возвращаемый тип будет использоваться в качестве ссылочного типа в созданном коде. Если возвращено значение null, тип не будет указан, и нужно создать новый тип. Если существует несколько суррогатов, можно выполнить сопоставление для каждого суррогатного типа обратно к исходному типу.
Параметр customData — это объект, который изначально был возвращён из GetCustomDataToExport. Это customData используется, когда суррогатные авторы хотят вставить дополнительные данные или указания в метаданные, которые будут использоваться во время импорта для создания кода.
Метод ProcessImportedType
Метод ProcessImportedType настраивает любой тип, созданный из импорта схемы. Этот метод является необязательным.
При импорте схемы этот метод позволяет настраивать любые импортированные данные типа и компиляции. Рассмотрим пример.
public System.CodeDom.CodeTypeDeclaration ProcessImportedType(System.CodeDom.CodeTypeDeclaration typeDeclaration, System.CodeDom.CodeCompileUnit compileUnit)
{
Console.WriteLine("ProcessImportedType");
foreach (CodeTypeMember member in typeDeclaration.Members)
{
object memberCustomData = member.UserData[typeof(IDataContractSurrogate)];
if (memberCustomData != null
&& memberCustomData is string
&& ((string)memberCustomData == "private"))
{
member.Attributes = ((member.Attributes & ~MemberAttributes.AccessMask) | MemberAttributes.Private);
}
}
return typeDeclaration;
}
Во время импорта этот метод вызывается для каждого типа, созданного. Измените указанный CodeTypeDeclaration или измените значение CodeCompileUnit. Это включает изменение имени, элементов, атрибутов и многих других свойств CodeTypeDeclarationобъекта. При обработке CodeCompileUnit можно изменить директивы, пространства имен, ссылочные сборки и некоторые другие аспекты.
Параметр CodeTypeDeclaration содержит объявление типа кода DOM. Параметр CodeCompileUnit позволяет изменить обработку кода. Возврат null приводит к отбрасыванию объявления типа. И наоборот, при возвращении CodeTypeDeclarationизменения сохраняются.
Если пользовательские данные вставляются во время экспорта метаданных, его необходимо предоставить пользователю во время импорта, чтобы его можно было использовать. Эти пользовательские данные можно использовать для подсказок модели программирования или других комментариев. Каждый экземпляр CodeTypeDeclaration и CodeTypeMember содержит пользовательские данные в качестве свойства UserData, с приведением к типу IDataContractSurrogate.
Приведенный выше пример выполняет некоторые изменения в импортированной схеме. Код сохраняет закрытые члены исходного типа с помощью суррогата. Модификатор доступа по умолчанию при импорте схемы public. Таким образом, все члены суррогатной схемы будут общедоступными, если только не изменены, как в этом примере. Во время экспорта пользовательские данные вставляются в метаданные с информацией о том, какие члены являются частными. Пример ищет пользовательские данные, проверяет, является ли модификатор доступа закрытым, а затем изменяет соответствующий член, чтобы быть закрытым, задав его атрибуты. Без этой настройки numpens член будет определен как публичный вместо приватного.
Метод GetKnownCustomDataTypes
Этот метод получает пользовательские типы данных, определенные из схемы. Этот метод является необязательным для импорта схемы.
Метод вызывается в начале экспорта и импорта схемы. Метод возвращает пользовательские типы данных, используемые в схеме, экспортированной или импортированной. Методу передается Collection<T> (параметр customDataTypes), который представляет собой коллекцию типов. Метод должен добавить в эту коллекцию дополнительные известные типы. Известные пользовательские типы данных необходимы для включения сериализации и десериализации пользовательских данных с помощью DataContractSerializer. Дополнительные сведения см. в разделе "Известные типы контракта данных".
Реализация суррогата
Чтобы использовать суррогат договора о данных в WCF, нужно следовать нескольким специальным процедурам.
Как использовать суррогат для сериализации и десериализации.
Используйте DataContractSerializer для выполнения сериализации и десериализации данных с использованием суррогата. Создается DataContractSerializer с помощью DataContractSerializerOperationBehavior. Суррогат также должен быть указан.
Реализация сериализации и десериализации
Создайте экземпляр ServiceHost для вашей службы. Полные инструкции см. в разделе "Базовое программирование WCF".
Для каждого ServiceEndpoint указанного узла службы найдите его OperationDescription.
Выполните поиск среди работа операций, чтобы определить, найден ли экземпляр DataContractSerializerOperationBehavior.
Если DataContractSerializerOperationBehavior обнаружен, задайте свойству DataContractSurrogate новый экземпляр суррогата. Если элемент DataContractSerializerOperationBehavior не найден, то создайте новый экземпляр и установите член DataContractSurrogate нового поведения в новый экземпляр суррогата.
Наконец, добавьте это новое поведение в текущее поведение операции, как показано в следующем примере:
using (ServiceHost serviceHost = new ServiceHost(typeof(InventoryCheck))) foreach (ServiceEndpoint ep in serviceHost.Description.Endpoints) { foreach (OperationDescription op in ep.Contract.Operations) { DataContractSerializerOperationBehavior dataContractBehavior = op.Behaviors.Find<DataContractSerializerOperationBehavior>() as DataContractSerializerOperationBehavior; if (dataContractBehavior != null) { dataContractBehavior.DataContractSurrogate = new InventorySurrogated(); } else { dataContractBehavior = new DataContractSerializerOperationBehavior(op); dataContractBehavior.DataContractSurrogate = new InventorySurrogated(); op.Behaviors.Add(dataContractBehavior); } } }
Использование суррогата для импорта метаданных
При импорте метаданных, таких как WSDL и XSD, для создания клиентского кода суррогат необходимо добавить в компонент, отвечающий за создание кода из схемы XSD. XsdDataContractImporter Для этого напрямую измените WsdlImporter, используемое для импорта метаданных.
Реализация суррогата для импорта метаданных
Импортируйте метаданные с помощью WsdlImporter класса.
TryGetValue Используйте метод, чтобы проверить, был ли определен объектXsdDataContractImporter.
TryGetValue Если метод возвращает
false, создайте новый XsdDataContractImporter и задайте его свойству Options значение нового экземпляра класса ImportOptions. В противном случае используйте импортер, возвращаемый параметромoutметода TryGetValue.Если XsdDataContractImporter не ImportOptions определено, задайте для свойства новый экземпляр класса ImportOptions.
Установите свойство DataContractSurrogateImportOptionsXsdDataContractImporter на новый экземпляр суррогата.
Добавьте XsdDataContractImporter к коллекции, возвращаемой свойством State объекта WsdlImporter (унаследованного от класса MetadataExporter.)
ImportAllContracts Используйте метод WsdlImporter импорта всех контрактов данных в схеме. На последнем шаге код создается из схем, загруженных посредством обращения к суррогату.
MetadataExchangeClient mexClient = new MetadataExchangeClient(metadataAddress); mexClient.ResolveMetadataReferences = true; MetadataSet metaDocs = mexClient.GetMetadata(); WsdlImporter importer = new WsdlImporter(metaDocs); object dataContractImporter; XsdDataContractImporter xsdInventoryImporter; if (!importer.State.TryGetValue(typeof(XsdDataContractImporter), out dataContractImporter)) xsdInventoryImporter = new XsdDataContractImporter(); xsdInventoryImporter = (XsdDataContractImporter)dataContractImporter; xsdInventoryImporter.Options ??= new ImportOptions(); xsdInventoryImporter.Options.DataContractSurrogate = new InventorySurrogated(); importer.State.Add(typeof(XsdDataContractImporter), xsdInventoryImporter); Collection<ContractDescription> contracts = importer.ImportAllContracts();
Использование суррогата для экспорта метаданных
По умолчанию при экспорте метаданных из WCF для службы необходимо создать схему WSDL и XSD. Суррогат необходимо добавить в компонент, отвечающий за создание схемы XSD для типов XsdDataContractExporterконтрактов данных. Для этого используйте поведение, которое реализует IWsdlExportExtension, чтобы изменить WsdlExporter, или напрямую измените WsdlExporter, используемые для экспорта метаданных.
Использование суррогата для экспорта метаданных
Создайте новый WsdlExporter или используйте параметр, переданный
wsdlExporterметоду ExportContract .Используйте функцию TryGetValue , чтобы проверить, определена ли функция XsdDataContractExporter .
Если TryGetValue возвращает
false, создайте новый XsdDataContractExporter с созданными XML-схемами из WsdlExporter, и добавьте ее в коллекцию, возвращенную свойством State объекта WsdlExporter. В противном случае используйте экспортер, который возвращается как результат параметраoutметода TryGetValue.XsdDataContractExporter Если для ExportOptions не задано значение, установите свойству Options новый экземпляр класса ExportOptions.
Установите свойство DataContractSurrogateExportOptionsXsdDataContractExporter на новый экземпляр суррогата. Последующие шаги по экспорту метаданных не требуют каких-либо изменений.
WsdlExporter exporter = new WsdlExporter(); //or //public void ExportContract(WsdlExporter exporter, // WsdlContractConversionContext context) { ... } object dataContractExporter; XsdDataContractExporter xsdInventoryExporter; if (!exporter.State.TryGetValue(typeof(XsdDataContractExporter), out dataContractExporter)) { xsdInventoryExporter = new XsdDataContractExporter(exporter.GeneratedXmlSchemas); } else { xsdInventoryExporter = (XsdDataContractExporter)dataContractExporter; } exporter.State.Add(typeof(XsdDataContractExporter), xsdInventoryExporter); if (xsdInventoryExporter.Options == null) xsdInventoryExporter.Options = new ExportOptions(); xsdInventoryExporter.Options.DataContractSurrogate = new InventorySurrogated();