Náhradní funkce DataContract

Ukázka DataContract ukazuje, jak lze procesy, jako je serializace, deserializace, export schématu a import schématu, přizpůsobit pomocí náhradní třídy kontraktu dat. Tato ukázka ukazuje, jak použít náhradník ve scénáři klienta a serveru, kde se data serializují a přenášejí mezi klientem a službou WCF (Windows Communication Foundation).

Poznámka:

Postup nastavení a pokyny k sestavení pro tuto ukázku najdete na konci tohoto tématu.

Ukázka používá následující kontrakt služby:

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

    [OperationContract]
    Employee GetEmployee(string name);
}

Tato AddEmployee operace umožňuje uživatelům přidávat data o nových zaměstnancích a GetEmployee operace podporuje vyhledávání zaměstnanců na základě jména.

Tyto operace používají následující datový typ:

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

    [DataMember]
    public Decimal salary;

    [DataMember]
    public Person person;
}

V tomto typu Employee nelze serializovat třídu Person (která je zobrazena v následujícím vzorovém kódu) pomocí DataContractSerializer, protože není platnou datovou kontraktovou třídou.

public class Person
{
    public string firstName;

    public string lastName;

    public int age;

    public Person() { }
}

Atribut můžete použít DataContractAttribute pro Person třídu, ale to není vždy možné. Třída může být například Person definována v samostatném sestavení, nad kterým nemáte žádný ovládací prvek.

Vzhledem k tomuto omezení je jedním ze způsobů, jak serializovat třídu Person, její nahrazení jinou třídou označenou DataContractAttribute a přenesení potřebných dat do nové třídy. Cílem je, aby třída Person vystupovala jako DataContract pro DataContractSerializer. Všimněte si, že toto je jeden ze způsobů, jak serializovat třídy bez datového kontraktu.

Ukázka logicky nahradí Person třídu jinou třídou s názvem PersonSurrogated.

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

    [DataMember]
    public string LastName;

    [DataMember]
    public int Age;
}

Zástupný datový kontrakt se používá k dosažení této náhrady. Náhradní kontrakt dat je třída, která implementuje IDataContractSurrogate. V této ukázce AllowNonSerializableTypesSurrogate třída implementuje toto rozhraní.

V implementaci rozhraní je prvním úkolem vytvořit mapování typu z Person do PersonSurrogated. Používá se při serializaci i v době exportu schématu. Toto mapování se dosahuje implementací GetDataContractType(Type) metody.

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

GetObjectToSerialize(Object, Type) Metoda mapuje Person instanci na PersonSurrogated instanci během serializace, jak je znázorněno v následujícím vzorovém kódu.

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) poskytuje zpětné mapování pro deserializaci, jak je znázorněno v následujícím vzorovém kódu.

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

Pokud chcete mapovat PersonSurrogated kontrakt dat na existující Person třídu během importu schématu, ukázka implementuje metodu GetReferencedTypeOnImport(String, String, Object) , jak je znázorněno v následujícím vzorovém kódu.

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

Následující ukázkový kód dokončí implementaci IDataContractSurrogate rozhraní.

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

V této ukázce je v ServiceModelu atributem AllowNonSerializableTypesAttribute povolen zástupce. Vývojáři by tento atribut potřebovali použít u kontraktu služeb, jak je znázorněno na výše uvedeném kontraktu IPersonnelDataService služby. Tento atribut implementuje IContractBehavior a nastavuje náhradní operace ve svých ApplyClientBehavior a ApplyDispatchBehavior metodách.

V tomto případě není atribut nutný – používá se pro demonstrační účely v této ukázce. Uživatelé mohou alternativně povolit náhradu ručním přidáním podobného IContractBehavior, IEndpointBehavior nebo IOperationBehavior pomocí kódu nebo konfigurace.

Implementace IContractBehavior vyhledává operace, které používají DataContract tím, že kontroluje, zda mají DataContractSerializerOperationBehavior zaregistrován. Pokud ano, nastaví vlastnost DataContractSurrogate pro toto chování. Následující ukázkový kód ukazuje, jak se to dělá. Nastavení zástupného objektu v tomto chování operace umožňuje serializaci a deserializaci.

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

Je potřeba provést další kroky pro připojení náhradních dat pro použití během generování metadat. Jedním z mechanismů, jak to udělat, je poskytnout IWsdlExportExtension, což tato ukázka demonstruje. Dalším způsobem je úprava WsdlExporter přímo.

Atribut AllowNonSerializableTypesAttribute implementuje IWsdlExportExtension a IContractBehavior. Rozšíření může být v tomto případě buď IContractBehavior, nebo IEndpointBehavior. Její IWsdlExportExtension.ExportContract implementace metody umožňuje zástupci, aby byl přidán do XsdDataContractExporter použitého v rámci generování schématu pro DataContract. Následující fragment kódu ukazuje, jak to udělat.

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

Když spustíte ukázku, klient zavolá AddEmployee následovaný voláním GetEmployeee, které zkontroluje, jestli bylo první volání úspěšné. Výsledek požadavku operace GetEmployeee se zobrazí v okně konzoly klienta. Operace GetEmployee musí být úspěšná při hledání zaměstnance a zobrazit "nalezen".

Poznámka:

Tato ukázka ukazuje, jak připojit náhradní položky pro serializaci, deserializaci a generování metadat. Nezobrazuje, jak připojit náhradní kód pro generování kódu z metadat. Pokud se chcete podívat na příklad, jak lze použít zástupce pro účely generování klientského kódu, podívejte se na ukázku vlastní publikace WSDL.

Jak nastavit, sestavit a spustit ukázku

  1. Ujistěte se, že jste provedli instalační proceduru One-Time pro ukázky Windows Communication Foundation.

  2. Pokud chcete sestavit C# verzi řešení, postupujte podle pokynů v Sestavení ukázek Windows Communication Foundation.

  3. Pokud chcete spustit ukázku v konfiguraci pro jeden počítač nebo pro více počítačů, postupujte podle pokynů v Spuštění ukázek Windows Communication Foundation.