DataContract 示例演示如何使用数据协定代理项类自定义序列化、反序列化、架构导出和架构导入等进程。 此示例演示如何在客户端和服务器方案中使用代理项,在 Windows Communication Foundation (WCF) 客户端和服务之间序列化和传输数据。
注释
本示例的设置过程和生成说明位于本主题末尾。
此示例使用以下服务协定:
[ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples")]
[AllowNonSerializableTypes]
public interface IPersonnelDataService
{
[OperationContract]
void AddEmployee(Employee employee);
[OperationContract]
Employee GetEmployee(string name);
}
该 AddEmployee
操作允许用户添加有关新员工的数据,而 GetEmployee
操作支持通过姓名搜索员工。
这些操作使用以下数据类型:
[DataContract(Namespace = "http://Microsoft.ServiceModel.Samples")]
class Employee
{
[DataMember]
public DateTime dateHired;
[DataMember]
public Decimal salary;
[DataMember]
public Person person;
}
在Employee
类型中,Person
类(如以下示例代码所示)无法由DataContractSerializer序列化,因为它不是有效的数据契约类。
public class Person
{
public string firstName;
public string lastName;
public int age;
public Person() { }
}
可以将DataContractAttribute属性应用于Person
类,但这并不总是可行。 例如,Person
类可以在一个你无法控制的独立程序集里定义。
鉴于此限制,序列化类的 Person
一种方法是将其替换为另一个标记的 DataContractAttribute 类,并将必要的数据复制到新类。 目标是使Person
类在DataContractSerializer中显示为 DataContract。 请注意,这是序列化非数据协定类的一种方法。
该示例以逻辑方式将 Person
类替换为另一个名为 PersonSurrogated
的类。
[DataContract(Name="Person", Namespace = "http://Microsoft.ServiceModel.Samples")]
public class PersonSurrogated
{
[DataMember]
public string FirstName;
[DataMember]
public string LastName;
[DataMember]
public int Age;
}
数据协定代理项用于实现此替换。 数据协定代理项是实现 IDataContractSurrogate 的类。 在此示例中, AllowNonSerializableTypesSurrogate
类实现此接口。
在接口实现中,第一个任务是建立从Person
到PersonSurrogated
的类型映射。 此方法既在进行序列化时使用,也在导出架构时使用。 此映射是通过实现GetDataContractType(Type)方法来完成的。
public Type GetDataContractType(Type type)
{
if (typeof(Person).IsAssignableFrom(type))
{
return typeof(PersonSurrogated);
}
return type;
}
此方法在 GetObjectToSerialize(Object, Type) 序列化过程中将 Person
实例映射到 PersonSurrogated
实例,如以下示例代码所示。
public object GetObjectToSerialize(object obj, Type targetType)
{
if (obj is Person)
{
Person person = (Person)obj;
PersonSurrogated personSurrogated = new PersonSurrogated();
personSurrogated.FirstName = person.firstName;
personSurrogated.LastName = person.lastName;
personSurrogated.Age = person.age;
return personSurrogated;
}
return obj;
}
该方法 GetDeserializedObject(Object, Type) 提供反序列化的反向映射,如以下示例代码所示。
public object GetDeserializedObject(object obj,
Type targetType)
{
if (obj is PersonSurrogated)
{
PersonSurrogated personSurrogated = (PersonSurrogated)obj;
Person person = new Person();
person.firstName = personSurrogated.FirstName;
person.lastName = personSurrogated.LastName;
person.age = personSurrogated.Age;
return person;
}
return obj;
}
若要在架构导入过程中将数据 PersonSurrogated
协定映射到现有 Person
类,此示例将实现 GetReferencedTypeOnImport(String, String, Object) 该方法,如以下示例代码所示。
public Type GetReferencedTypeOnImport(string typeName,
string typeNamespace, object customData)
{
if (
typeNamespace.Equals("http://schemas.datacontract.org/2004/07/DCSurrogateSample")
)
{
if (typeName.Equals("PersonSurrogated"))
{
return typeof(Person);
}
}
return null;
}
以下示例代码完成接口的 IDataContractSurrogate 实现。
public System.CodeDom.CodeTypeDeclaration ProcessImportedType(
System.CodeDom.CodeTypeDeclaration typeDeclaration,
System.CodeDom.CodeCompileUnit compileUnit)
{
return typeDeclaration;
}
public object GetCustomDataToExport(Type clrType,
Type dataContractType)
{
return null;
}
public object GetCustomDataToExport(
System.Reflection.MemberInfo memberInfo, Type dataContractType)
{
return null;
}
public void GetKnownCustomDataTypes(
KnownTypeCollection customDataTypes)
{
// It does not matter what we do here.
throw new NotImplementedException();
}
在此示例中,代理项是通过名为 AllowNonSerializableTypesAttribute
的属性在 ServiceModel 中启用的。 开发人员需要对其服务协定应用此属性,如上述服务协定所示 IPersonnelDataService
。 此属性实现 IContractBehavior
并在其 ApplyClientBehavior
和 ApplyDispatchBehavior
方法中对操作设置该代理项。
在本例中不需要该属性 - 它用于此示例中的演示目的。 用户可以手动添加类似 IContractBehavior
、IEndpointBehavior
或IOperationBehavior
的代码,也可以通过配置来启用替代项。
IContractBehavior
实现通过检查操作是否已注册 DataContractSerializerOperationBehavior
来查找使用 DataContract 的操作。 如果这样做,它将为该行为设置DataContractSurrogate
属性。 以下示例代码演示如何执行此作。 在此操作行为上设置代理项可使其进行序列化和反序列化。
public void ApplyClientBehavior(ContractDescription description, ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime proxy)
{
foreach (OperationDescription opDesc in description.Operations)
{
ApplyDataContractSurrogate(opDesc);
}
}
public void ApplyDispatchBehavior(ContractDescription description, ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.DispatchRuntime dispatch)
{
foreach (OperationDescription opDesc in description.Operations)
{
ApplyDataContractSurrogate(opDesc);
}
}
private static void ApplyDataContractSurrogate(OperationDescription description)
{
DataContractSerializerOperationBehavior dcsOperationBehavior = description.Behaviors.Find<DataContractSerializerOperationBehavior>();
if (dcsOperationBehavior != null)
{
if (dcsOperationBehavior.DataContractSurrogate == null)
dcsOperationBehavior.DataContractSurrogate = new AllowNonSerializableTypesSurrogate();
}
}
需要采取其他步骤来插入代理项,以便在元数据生成过程中使用。 执行此操作的一种机制是提供一个 IWsdlExportExtension
,正如本示例所展示的那样。 另一种方法是直接修改 WsdlExporter
。
属性 AllowNonSerializableTypesAttribute
实现 IWsdlExportExtension
和 IContractBehavior
。 扩展在本例中可以是IContractBehavior
或IEndpointBehavior
。 其 IWsdlExportExtension.ExportContract
方法实现通过将代理项添加到为 DataContract 生成架构的过程中使用的XsdDataContractExporter
来启用该代理项。 以下代码片段演示如何执行此作。
public void ExportContract(WsdlExporter exporter, WsdlContractConversionContext context)
{
if (exporter == null)
throw new ArgumentNullException("exporter");
object dataContractExporter;
XsdDataContractExporter xsdDCExporter;
if (!exporter.State.TryGetValue(typeof(XsdDataContractExporter), out dataContractExporter))
{
xsdDCExporter = new XsdDataContractExporter(exporter.GeneratedXmlSchemas);
exporter.State.Add(typeof(XsdDataContractExporter), xsdDCExporter);
}
else
{
xsdDCExporter = (XsdDataContractExporter)dataContractExporter;
}
if (xsdDCExporter.Options == null)
xsdDCExporter.Options = new ExportOptions();
if (xsdDCExporter.Options.DataContractSurrogate == null)
xsdDCExporter.Options.DataContractSurrogate = new AllowNonSerializableTypesSurrogate();
}
运行示例时,客户端将调用 AddEmployee,然后调用 GetEmployee 调用以检查第一次调用是否成功。 GetEmployee操作请求的结果显示在客户端控制台窗口中。 GetEmployee 操作必须成功找到雇员并打印“found”。
注释
此示例演示如何插入代理项进行序列化、反序列化和元数据生成。 它不显示如何插入代理项,以便从元数据生成代码。 若要查看代理如何用于客户端代码生成,请参阅 自定义 WSDL 发布 示例。
设置、生成和运行示例
确保已为 Windows Communication Foundation 示例 执行One-Time 安装过程。
若要生成解决方案的 C# 版本,请按照 生成 Windows Communication Foundation 示例中的说明进行作。
若要在单台计算机或跨计算机配置中运行示例,请按照 运行 Windows Communication Foundation 示例中的说明进行操作。