Udostępnij przez


Podmiot zastępczy DataContract

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ć obiektu zastępczego w scenariuszu działania klienta i serwera, w którym dane są serializowane i przesyłane między klientem i usługą Windows Communication Foundation (WCF).

Uwaga / Notatka

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

Klasa Employee w typie Person (pokazana w poniższym przykładzie kodu) nie może być serializowana przez DataContractSerializer, ponieważ nie jest to prawidłowa klasa kontraktu danych.

public class Person
{
    public string firstName;

    public string lastName;

    public int age;

    public Person() { }
}

Do klasy DataContractAttribute można zastosować atrybut Person, 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, aby klasa Person była traktowana jako DataContract przez 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;
}

Substytut kontraktu danych jest używany do realizacji tego zastąpienia. Surogat kontraktu danych to klasa, która implementuje element IDataContractSurrogate. 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) odwzorowuje wystąpienie Person na instancję PersonSurrogated podczas serializacji, jak pokazano w przykładowym kodzie poniżej.

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 na potrzeby 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 IContractBehavior i konfiguruje zastępcę dla operacji w swoich 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ą alternatywnie włączyć zastępczą metodę, ręcznie dodając podobne elementy IContractBehavior, IEndpointBehavior lub IOperationBehavior za pomocą kodu lub konfiguracji.

Implementacja IContractBehavior wyszukuje operacje korzystające z DataContract, sprawdzając, czy mają DataContractSerializerOperationBehavior zarejestrowane. Kiedy tak się dzieje, właściwość DataContractSurrogate zostaje ustawiona na to zachowanie. 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 jest dostarczenie IWsdlExportExtension, co demonstruje ten przykład. Innym sposobem jest bezpośrednie zmodyfikowanie elementu WsdlExporter .

Atrybut AllowNonSerializableTypesAttribute implementuje IWsdlExportExtension i IContractBehavior. Rozszerzenie może być rozszerzeniem IContractBehavior lub IEndpointBehavior w tym przypadku. Jego IWsdlExportExtension.ExportContract implementacja metody umożliwia obiekt zastępczy, poprzez dodanie go do używanego XsdDataContractExporter do generowania schematu DataContractu. 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();
}

Podczas uruchamiania przykładu, klient wywołuje metodę AddEmployee, a następnie metodę 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 "znaleziono".

Uwaga / Notatka

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 obiektu zastępczego do integracji z kodem generowanym przez klienta, zobacz przykładową niestandardową publikację WSDL.

Aby skonfigurować, skompilować i uruchomić przykładowy program

  1. Upewnij się, że wykonano procedurę instalacji One-Time 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 Uruchamianie przykładów programu Windows Communication Foundation.