DataContract 代理项

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 类实现此接口。

在接口实现中,第一个任务是建立从PersonPersonSurrogated的类型映射。 此方法既在进行序列化时使用,也在导出架构时使用。 此映射是通过实现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 并在其 ApplyClientBehaviorApplyDispatchBehavior 方法中对操作设置该代理项。

在本例中不需要该属性 - 它用于此示例中的演示目的。 用户可以手动添加类似 IContractBehaviorIEndpointBehaviorIOperationBehavior 的代码,也可以通过配置来启用替代项。

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 实现 IWsdlExportExtensionIContractBehavior。 扩展在本例中可以是IContractBehaviorIEndpointBehavior。 其 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 发布 示例。

设置、生成和运行示例

  1. 确保已为 Windows Communication Foundation 示例 执行One-Time 安装过程。

  2. 若要生成解决方案的 C# 版本,请按照 生成 Windows Communication Foundation 示例中的说明进行作。

  3. 若要在单台计算机或跨计算机配置中运行示例,请按照 运行 Windows Communication Foundation 示例中的说明进行操作。