Dela via


DataContract Surrogate

DataContract-exemplet visar hur processer som serialisering, deserialisering, schemaexport och schemaimport kan anpassas med hjälp av en surrogatklass för datakontrakt. Det här exemplet visar hur du använder en surrogat i ett klient- och serverscenario där data serialiseras och överförs mellan en Windows Communication Foundation-klient (WCF) klient och tjänst.

Anmärkning

Installationsproceduren och bygginstruktionerna för det här exemplet finns i slutet av det här avsnittet.

Exemplet använder följande tjänstkontrakt:

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

    [OperationContract]
    Employee GetEmployee(string name);
}

Åtgärden AddEmployee gör det möjligt för användare att lägga till data om nya anställda och åtgärden GetEmployee stöder sökning efter anställda baserat på namn.

Dessa åtgärder använder följande datatyp:

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

    [DataMember]
    public Decimal salary;

    [DataMember]
    public Person person;
}

I typen EmployeePerson kan klassen (som visas i följande exempelkod) inte serialiseras av DataContractSerializer eftersom den inte är en giltig datakontraktsklass.

public class Person
{
    public string firstName;

    public string lastName;

    public int age;

    public Person() { }
}

Du kan använda DataContractAttribute attributet för Person klassen, men det är inte alltid möjligt. Klassen Person kan till exempel definieras i en separat sammansättning som du inte har någon kontroll över.

Med den här begränsningen är ett sätt att serialisera klassen att ersätta den Person med en annan klass som är markerad med DataContractAttribute och kopiera över nödvändiga data till den nya klassen. Målet är att få Person klassen att visas som en DataContract till DataContractSerializer. Observera att det här är ett sätt att serialisera icke-datakontraktsklasser.

Exemplet ersätter Person logiskt klassen med en annan klass med namnet PersonSurrogated.

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

    [DataMember]
    public string LastName;

    [DataMember]
    public int Age;
}

Surrogat för dataavtal används för att genomföra denna ersättning. En surrogat för datakontrakt är en klass som implementerar IDataContractSurrogate. I det här exemplet AllowNonSerializableTypesSurrogate implementerar klassen det här gränssnittet.

I gränssnittsimplementeringen är den första uppgiften att upprätta en typmappning från Person till PersonSurrogated. Detta används både vid serialiseringstid och vid schemaexport. Den här mappningen uppnås genom att implementera metoden GetDataContractType(Type).

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

Metoden GetObjectToSerialize(Object, Type) mappar en Person instans till en PersonSurrogated instans under serialiseringen, enligt följande exempelkod.

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

Metoden GetDeserializedObject(Object, Type) tillhandahåller omvänd mappning för deserialisering, enligt följande exempelkod.

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

Om du vill mappa datakontraktet PersonSurrogated till den befintliga Person klassen under schemaimporten implementerar GetReferencedTypeOnImport(String, String, Object) exemplet metoden, enligt följande exempelkod.

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

Följande exempelkod slutför implementeringen av IDataContractSurrogate gränssnittet.

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

I det här exemplet aktiveras surrogaten i ServiceModel av ett attribut som heter AllowNonSerializableTypesAttribute. Utvecklare skulle behöva tillämpa det här attributet på sitt tjänstkontrakt enligt beskrivningen i tjänstkontraktet IPersonnelDataService ovan. Det här attributet implementerar IContractBehavior och konfigurerar surrogat på operationer i dess ApplyClientBehavior och ApplyDispatchBehavior metoder.

Attributet är inte nödvändigt i det här fallet – det används i demonstrationssyfte i det här exemplet. Användare kan också aktivera en surrogat genom att manuellt lägga till en liknande IContractBehavior, IEndpointBehavior eller IOperationBehavior med hjälp av kod eller med hjälp av konfiguration.

Implementeringen IContractBehavior söker efter åtgärder som använder DataContract genom att kontrollera om de har en DataContractSerializerOperationBehavior registrerad. Om de gör det, ställs egenskapen DataContractSurrogate för det beteendet in. Följande exempelkod visar hur det går till. Genom att ställa in surrogatet för detta åtgärdsbeteende möjliggörs serialisering och deserialisering.

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

Ytterligare åtgärder måste vidtas för att ansluta surrogaten för användning under generering av metadata. En metod för att göra detta är att tillhandahålla en IWsdlExportExtension som är vad det här exemplet visar. Ett annat sätt är att ändra WsdlExporter direkt.

Attributet AllowNonSerializableTypesAttribute implementerar IWsdlExportExtension och IContractBehavior. Tillägget kan vara antingen en IContractBehavior eller IEndpointBehavior i det här fallet. Dess IWsdlExportExtension.ExportContract metodimplementering gör det möjligt för surrogaten genom att lägga till den i den XsdDataContractExporter som används under schemagenereringen för DataContract. Följande kodfragment visar hur du gör detta.

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

När du kör exemplet anropar klienten AddEmployee följt av ett GetEmployee-anrop för att kontrollera om det första anropet lyckades. Resultatet av getEmployee-åtgärdsbegäran visas i klientkonsolfönstret. Åtgärden GetEmployee måste lyckas hitta medarbetaren och skriva ut "hittad".

Anmärkning

Det här exemplet visar hur du ansluter en surrogat för serialisering, deserialisering och metadatagenerering. Den visar inte hur du ansluter en surrogat för kodgenerering från metadata. Ett exempel på hur en surrogat kan användas för att ansluta till klientkodgenerering finns i exemplet på anpassad WSDL-publikation .

Så här konfigurerar du, skapar och kör exemplet

  1. Kontrollera att du har utfört One-Time installationsproceduren för Windows Communication Foundation-exempel.

  2. Om du vill skapa C#-utgåvan av lösningen följer du anvisningarna i Skapa Windows Communication Foundation-exempel.

  3. Om du vill köra exemplet i en konfiguration med en eller flera datorer följer du anvisningarna i Köra Windows Communication Foundation-exempel.