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();
}
在此範例中,Surrogate 是由名為 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 操作必須成功尋找員工並表示「找到」。
備註
此範例顯示如何導入替代物以進行序列化、解除序列化和元數據生成。 它並未顯示如何使用替代物來從元數據生成程式碼。 若要查看代理程式如何用來整合到用戶端程式碼生成的範例,請參閱 客製化 WSDL 發佈 範例。
要設定、建置和執行範例,請執行以下步驟:
請確定您已針對 Windows Communication Foundation 範例 執行One-Time 安裝程式。
若要建置解決方案的 C# 版本,請遵循 建置 Windows Communication Foundation 範例中的指示。
若要在單一或跨計算機組態中執行範例,請遵循執行 Windows Communication Foundation 範例 中的指示。