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
クラスをシリアル化する方法の 1 つは、 DataContractAttribute でマークされた別のクラスに置き換え、必要なデータを新しいクラスにコピーすることです。 目的は、 Person
クラスを DataContractSerializerの DataContract として表示することです。 これは、非データ コントラクト クラスをシリアル化する 1 つの方法であることに注意してください。
このサンプルでは、 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();
}
このサンプルでは、serviceModel では、 AllowNonSerializableTypesAttribute
という属性によってサロゲートが有効になっています。 開発者は、上記の IPersonnelDataService
サービス コントラクトに示すように、サービス コントラクトにこの属性を適用する必要があります。 この属性は、 IContractBehavior
を実装し、その ApplyClientBehavior
メソッドと ApplyDispatchBehavior
メソッドで操作のサロゲートを設定します。
この場合、属性は必要ありません。このサンプルでは、デモンストレーション目的で使用されます。 ユーザーは、コードまたは構成を使用して、同様の IContractBehavior
、 IEndpointBehavior
、または IOperationBehavior
を手動で追加してサロゲートを有効にすることもできます。
IContractBehavior
実装は、データコントラクトを使用する操作を、DataContractSerializerOperationBehavior
が登録されているかどうかを確認することで検索します。 その場合、その動作に対して 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();
}
}
メタデータの生成中に使用するためにサロゲートをプラグインするには、追加の手順を実行する必要があります。 これを行うメカニズムの 1 つは、このサンプルが示す IWsdlExportExtension
を提供する方法です。 もう 1 つの方法は、 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 Samplesの実行」の手順に従います。