次の方法で共有


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 クラスをシリアル化する方法の 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 メソッドで操作のサロゲートを設定します。

この場合、属性は必要ありません。このサンプルでは、デモンストレーション目的で使用されます。 ユーザーは、コードまたは構成を使用して、同様の IContractBehaviorIEndpointBehavior 、または 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属性は、IWsdlExportExtensionIContractBehaviorを実装します。 この場合、拡張機能は 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 パブリケーション のサンプルを参照してください。

サンプルを設定、ビルド、実行するには

  1. Windows Communication Foundation サンプル One-Time セットアップ手順を実行していることを確認します。

  2. ソリューションの C# エディションをビルドするには、「 Windows Communication Foundation サンプルのビルド」の手順に従います。

  3. 単一または複数のコンピューター間の構成でサンプルを実行するには、「Windows Communication Foundation Samplesの実行」の手順に従います。