DataContract — surogat

W przykładzie DataContract pokazano, jak procesy, takie jak serializacja, deserializacja, eksport schematu i importowanie schematu, można dostosować przy użyciu klasy zastępczej kontraktu danych. W tym przykładzie pokazano, jak używać zastępczego w scenariuszu klienta i serwera, w którym dane są serializowane i przesyłane między klientem i usługą programu Windows Communication Foundation (WCF).

Uwaga

Procedura instalacji i instrukcje kompilacji dla tego przykładu znajdują się na końcu tego tematu.

W przykładzie użyto następującego kontraktu usługi:

[ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples")]
[AllowNonSerializableTypes]
public interface IPersonnelDataService
{
    [OperationContract]
    void AddEmployee(Employee employee);

    [OperationContract]
    Employee GetEmployee(string name);
}

Operacja AddEmployee umożliwia użytkownikom dodawanie danych o nowych pracownikach, a GetEmployee operacja obsługuje wyszukiwanie pracowników na podstawie nazwy.

Te operacje używają następującego typu danych:

[DataContract(Namespace = "http://Microsoft.ServiceModel.Samples")]
class Employee
{
    [DataMember]
    public DateTime dateHired;

    [DataMember]
    public Decimal salary;

    [DataMember]
    public Person person;
}

W typie Employee klasa (pokazana w poniższym przykładowym kodzie) nie może być serializowana przez DataContractSerializer klasę, Person ponieważ nie jest to prawidłowa klasa kontraktu danych.

public class Person
{
    public string firstName;

    public string lastName;

    public int age;

    public Person() { }
}

Atrybut można zastosować DataContractAttribute do Person klasy, ale nie zawsze jest to możliwe. Na przykład klasę Person można zdefiniować w osobnym zestawie, nad którym nie masz żadnej kontroli.

Biorąc pod uwagę to ograniczenie, jednym ze sposobów serializacji Person klasy jest zastąpienie jej inną klasą oznaczoną DataContractAttribute i skopiowanie niezbędnych danych do nowej klasy. Celem jest, Person aby klasa wyglądała jako element DataContract na DataContractSerializer. Należy pamiętać, że jest to jeden ze sposobów serializacji klas kontraktów innych niż dane.

Przykład logicznie zastępuje klasę Person inną klasą o nazwie PersonSurrogated.

[DataContract(Name="Person", Namespace = "http://Microsoft.ServiceModel.Samples")]
public class PersonSurrogated
{
    [DataMember]
    public string FirstName;

    [DataMember]
    public string LastName;

    [DataMember]
    public int Age;
}

Zastępca kontraktu danych jest używany do osiągnięcia tego zastąpienia. Surogat kontraktu danych to klasa, która implementuje IDataContractSurrogateelement . W tym przykładzie AllowNonSerializableTypesSurrogate klasa implementuje ten interfejs.

W implementacji interfejsu pierwszym zadaniem jest ustanowienie mapowania typów z Person do PersonSurrogated. Jest to używane zarówno w czasie serializacji, jak i w czasie eksportu schematu. To mapowanie jest osiągane przez zaimplementowanie GetDataContractType(Type) metody .

public Type GetDataContractType(Type type)
{
    if (typeof(Person).IsAssignableFrom(type))
    {
        return typeof(PersonSurrogated);
    }
    return type;
}

Metoda GetObjectToSerialize(Object, Type) mapuje Person wystąpienie na PersonSurrogated wystąpienie podczas serializacji, jak pokazano w poniższym przykładowym kodzie.

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

Metoda GetDeserializedObject(Object, Type) udostępnia odwrotne mapowanie deserializacji, jak pokazano w poniższym przykładowym kodzie.

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

Aby zamapować PersonSurrogated kontrakt danych na istniejącą Person klasę podczas importowania schematu, przykład implementuje metodę GetReferencedTypeOnImport(String, String, Object) , jak pokazano w poniższym przykładowym kodzie.

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

Poniższy przykładowy kod kończy implementację interfejsu 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();
}

W tym przykładzie zastępca jest włączony w usłudze ServiceModel przez atrybut o nazwie AllowNonSerializableTypesAttribute. Deweloperzy musieliby zastosować ten atrybut w umowie IPersonnelDataService dotyczącej usług, jak pokazano w powyższym kontrakcie usługi. Ten atrybut implementuje i konfiguruje IContractBehavior zastępcę operacji w jego metodach ApplyClientBehavior i ApplyDispatchBehavior .

Atrybut nie jest niezbędny w tym przypadku — jest używany do celów demonstracyjnych w tym przykładzie. Użytkownicy mogą również włączyć zastępcę, ręcznie dodając podobny IContractBehaviorIEndpointBehavior kod lub IOperationBehavior używając konfiguracji.

Implementacja IContractBehavior wyszukuje operacje korzystające z elementu DataContract, sprawdzając, czy zostały DataContractSerializerOperationBehavior zarejestrowane. Jeśli tak, ustawia DataContractSurrogate właściwość tego zachowania. Poniższy przykładowy kod pokazuje, jak to zrobić. Ustawienie zastępczego tego zachowania operacji umożliwia jego serializacji i deserializacji.

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

Aby podłączyć surogat do użycia podczas generowania metadanych, należy wykonać dodatkowe kroki. Jednym z mechanizmów w tym celu jest dostarczenie tego IWsdlExportExtension , co pokazuje ten przykład. Innym sposobem jest bezpośrednie zmodyfikowanie elementu WsdlExporter .

Atrybut AllowNonSerializableTypesAttribute implementuje IWsdlExportExtension wartości i IContractBehavior. Rozszerzenie może być rozszerzeniem IContractBehavior lub IEndpointBehavior w tym przypadku. Jego IWsdlExportExtension.ExportContract implementacja metody umożliwia surogat, dodając go do używanego XsdDataContractExporter podczas generowania schematu dla elementu DataContract. Poniższy fragment kodu pokazuje, jak to zrobić.

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

Po uruchomieniu przykładu klient wywołuje metodę AddEmployee, a następnie wywołanie GetEmployee, aby sprawdzić, czy pierwsze wywołanie zakończyło się pomyślnie. Wynik żądania operacji GetEmployee jest wyświetlany w oknie konsoli klienta. Operacja GetEmployee musi zakończyć się powodzeniem w znalezieniu pracownika i wydrukowaniu "found".

Uwaga

W tym przykładzie pokazano, jak podłączyć surogat do serializacji, deserializacji i generowania metadanych. Nie pokazuje, jak podłączyć zastępcę do generowania kodu z metadanych. Aby zobaczyć przykład użycia zastępczego do podłączania kodu klienta, zobacz przykład niestandardowej publikacji WSDL.

Aby skonfigurować, skompilować i uruchomić przykład

  1. Upewnij się, że wykonano procedurę instalacji jednorazowej dla przykładów programu Windows Communication Foundation.

  2. Aby utworzyć wersję języka C# rozwiązania, postępuj zgodnie z instrukcjami w temacie Building the Windows Communication Foundation Samples (Tworzenie przykładów programu Windows Communication Foundation).

  3. Aby uruchomić przykład w konfiguracji pojedynczej lub między maszynami, postępuj zgodnie z instrukcjami w temacie Uruchamianie przykładów programu Windows Communication Foundation.