共用方式為


DataContract Surrogate (資料契約替代者)

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;
}

在 型別中 EmployeePerson 類別(如下列範例程式代碼所示)無法由 DataContractSerializer 串行化,因為它不是有效的數據合約類別。

public class Person
{
    public string firstName;

    public string lastName;

    public int age;

    public Person() { }
}

您可以將 屬性套用 DataContractAttributePerson 類別,但這不一定可行。 例如,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();
}

在此範例中,Surrogate 是由名為 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 操作必須成功尋找員工並表示「找到」。

備註

此範例顯示如何導入替代物以進行序列化、解除序列化和元數據生成。 它並未顯示如何使用替代物來從元數據生成程式碼。 若要查看代理程式如何用來整合到用戶端程式碼生成的範例,請參閱 客製化 WSDL 發佈 範例。

要設定、建置和執行範例,請執行以下步驟:

  1. 請確定您已針對 Windows Communication Foundation 範例 執行One-Time 安裝程式。

  2. 若要建置解決方案的 C# 版本,請遵循 建置 Windows Communication Foundation 範例中的指示。

  3. 若要在單一或跨計算機組態中執行範例,請遵循執行 Windows Communication Foundation 範例 中的指示。