Sdílet prostřednictvím


Náhrady kontraktů dat

Náhradní kontrakt dat je pokročilá funkce založená na modelu kontraktu dat. Tato funkce je navržená tak, aby se používala pro přizpůsobení a náhradu typu v situacích, kdy uživatelé chtějí změnit způsob serializace, deserializace nebo promítání do metadat. Některé scénáře, kdy je možné použít náhradník, je situace, kdy nebyl zadán kontrakt dat pro typ, pole a vlastnosti nejsou označeny atributem DataMemberAttribute nebo uživatelé chtějí dynamicky vytvářet varianty schématu.

Serializace a deserializace se provádí s náhradními kontrakty dat při použití DataContractSerializer k převodu z rozhraní .NET Framework do vhodného formátu, například XML. Náhradní kontrakty dat lze použít také k úpravě metadat exportovaných pro typy při vytváření reprezentací metadat, jako jsou dokumenty schématu XML (XSD). Při importu se kód vytvoří z metadat a náhradní kód lze v tomto případě použít k přizpůsobení vygenerovaného kódu.

Jak náhradník funguje

Náhradní funkce funguje mapováním jednoho typu ("původního" typu) na jiný typ ("náhradní" typ). Následující příklad ukazuje původní typ Inventory a nový náhradní InventorySurrogated typ. Typ Inventory není serializovatelný, ale InventorySurrogated typ je:

public class Inventory
{
    public int pencils;
    public int pens;
    public int paper;
}

Vzhledem k tomu, že pro tuto třídu nebyl definován datový kontrakt, převeďte třídu na náhradní třídu s datovým kontraktem. Náhradní třída je uvedena v následujícím příkladu:

[DataContract(Name = "Inventory")]
public class InventorySurrogated
{
    [DataMember]
    public int numpencils;
    [DataMember]
    public int numpaper;
    [DataMember]
    private int numpens;

    public int pens
    {
        get { return numpens; }
        set { numpens = value; }
    }
}

Implementace IDataContractSurrogate

Pokud chcete použít náhradní kontrakt dat, implementujte IDataContractSurrogate rozhraní.

Následuje přehled jednotlivých metod IDataContractSurrogate s možnou implementací.

GetDataContractType

Metoda GetDataContractType mapuje jeden typ na jiný. Tato metoda se vyžaduje pro serializaci, deserializaci, import a export.

První úkol definuje, jaké typy budou mapovány na jiné typy. Příklad:

public Type GetDataContractType(Type type)
{
    Console.WriteLine("GetDataContractType");
    if (typeof(Inventory).IsAssignableFrom(type))
    {
        return typeof(InventorySurrogated);
    }
    return type;
}
  • Při serializaci se mapování vrácené touto metodou následně používá k transformaci původní instance na náhradní instanci voláním GetObjectToSerialize metody.

  • Při deserializaci se mapování vrácené touto metodou používá serializátor k deserializaci do instance náhradního typu. Následně volá GetDeserializedObject transformaci náhradní instance na instanci původního typu.

  • Při exportu se náhradní typ vrácený touto metodou odráží, aby získal kontrakt dat, který se má použít pro generování metadat.

  • Při importu se počáteční typ změní na náhradní typ, který se projeví, aby se kontrakt dat použil pro účely, jako je odkazování na podporu.

Parametr Type je typ objektu, který je serializován, deserializován, importován nebo exportován. Metoda GetDataContractType musí vrátit vstupní typ, pokud náhradní nezpracuje typ. V opačném případě vraťte odpovídající náhradní typ. Pokud existuje několik náhradních typů, lze v této metodě definovat mnoho mapování.

Metoda GetDataContractType není volána pro předdefinované primitivy kontraktů dat, například Int32 nebo String. U jiných typů, jako jsou pole, uživatelem definované typy a další datové struktury, bude tato metoda volána pro každý typ.

V předchozím příkladu metoda zkontroluje, jestli type je parametr Inventory srovnatelný. Pokud ano, metoda ji mapuje na InventorySurrogated. Při každém zavolání serializace, deserializace, import schématu nebo schématu exportu se tato funkce volá jako první k určení mapování mezi typy.

GetObjectToSerialize – metoda

Metoda GetObjectToSerialize převede původní instanci typu na instanci náhradního typu. Metoda je vyžadována pro serializaci.

Dalším krokem je definování způsobu mapování fyzických dat z původní instance na náhradní metodu GetObjectToSerialize . Příklad:

public object GetObjectToSerialize(object obj, Type targetType)
{
    Console.WriteLine("GetObjectToSerialize");
    if (obj is Inventory)
    {
        InventorySurrogated isur = new InventorySurrogated();
        isur.numpaper = ((Inventory)obj).paper;
        isur.numpencils = ((Inventory)obj).pencils;
        isur.pens = ((Inventory)obj).pens;
        return isur;
    }
    return obj;
}

Metoda GetObjectToSerialize je volána při serializaci objektu. Tato metoda přenáší data z původního typu do polí náhradního typu. Pole mohou být přímo mapována na náhradní pole nebo manipulace s původními daty mohou být uložena v náhradních polích. Mezi možná použití patří: přímé mapování polí, provádění operací s daty, která mají být uložena v náhradních polích, nebo uložení XML původního typu do náhradního pole.

Parametr targetType odkazuje na deklarovaný typ členu. Tento parametr je náhradní typ vrácený metodou GetDataContractType . Serializátor nevynucuje, aby vrácený objekt je přiřazen k tomuto typu. Parametr obj je objekt pro serializaci a v případě potřeby bude převeden na jeho náhradní. Tato metoda musí vrátit vstupní objekt, pokud náhradní nezpracuje objekt. V opačném případě se vrátí nový náhradní objekt. Náhradní není volána, pokud je objekt null. V rámci této metody může být definováno množství náhradních mapování pro různé instance.

Při vytváření DataContractSerializermůžete dát pokyn k zachování odkazů na objekty. (Další informace najdete v tématu Serializace a deserializace.) To se provádí nastavením parametru v jeho konstruktoru preserveObjectReferences na true. V takovém případě je náhradní volána pouze jednou pro objekt, protože všechny následné serializace pouze zapisují odkaz do datového proudu. Pokud preserveObjectReferences je nastavena na false, náhradní je volána při každém výskytu instance.

Pokud se typ instance serializované liší od deklarovaného typu, informace o typu jsou zapsány do datového proudu, například, xsi:type aby instance byla deserializována na druhém konci. K tomuto procesu dochází bez ohledu na to, zda je objekt náhradní nebo ne.

Výše uvedený příklad převede data Inventory instance na instanci .InventorySurrogated Zkontroluje typ objektu a provede potřebné manipulace s převodem na náhradní typ. V tomto případě se pole Inventory třídy přímo zkopírují do InventorySurrogated polí třídy.

GetDeserializedObject – metoda

Metoda GetDeserializedObject převede náhradní instanci typu na původní instanci typu. Vyžaduje se pro deserializaci.

Dalším úkolem je definovat způsob mapování fyzických dat z náhradní instance na původní. Příklad:

public object GetDeserializedObject(object obj, Type targetType)
{
    Console.WriteLine("GetDeserializedObject");
    if (obj is InventorySurrogated)
    {
        Inventory invent = new Inventory();
        invent.pens = ((InventorySurrogated)obj).pens;
        invent.pencils = ((InventorySurrogated)obj).numpencils;
        invent.paper = ((InventorySurrogated)obj).numpaper;
        return invent;
    }
    return obj;
}

Tato metoda je volána pouze během deserializace objektu. Poskytuje zpětné mapování dat pro deserializaci z náhradního typu zpět do původního typu. Podobně jako u GetObjectToSerialize metody mohou být některá možná použití k přímému výměně dat polí, provádění operací s daty a ukládání dat XML. Při deserializaci nemusíte vždy získat přesné hodnoty dat z původního důvodu manipulace s převodem dat.

Parametr targetType odkazuje na deklarovaný typ členu. Tento parametr je náhradní typ vrácený metodou GetDataContractType . Parametr obj odkazuje na objekt, který byl deserializován. Objekt lze převést zpět na původní typ, pokud je náhradní. Tato metoda vrátí vstupní objekt, pokud náhradní nezpracuje objekt. V opačném případě se deserializovaný objekt vrátí po dokončení jeho převodu. Pokud existuje několik náhradních typů, můžete poskytnout převod dat z náhradního na primární typ pro každý tím, že u každého typu a jeho převodu.

Při vrácení objektu se interní tabulky objektů aktualizují o objekt vrácený touto náhradní. Všechny následné odkazy na instanci z tabulek objektů získají náhradní instanci.

Předchozí příklad převede objekty typu InventorySurrogated zpět na počáteční typ Inventory. V tomto případě se data přímo přenesou zpět do InventorySurrogated příslušných polí v Inventorysouboru . Vzhledem k tomu, že neexistují žádné manipulace s daty, budou každá pole členů obsahovat stejné hodnoty jako před serializací.

GetCustomDataToExport – metoda

Při exportu schématu GetCustomDataToExport je metoda volitelná. Slouží k vložení dalších dat nebo tipů do exportovaného schématu. Další data lze vložit na úroveň členu nebo typ. Příklad:

public object GetCustomDataToExport(System.Reflection.MemberInfo memberInfo, Type dataContractType)
{
    Console.WriteLine("GetCustomDataToExport(Member)");
    System.Reflection.FieldInfo fieldInfo = (System.Reflection.FieldInfo)memberInfo;
    if (fieldInfo.IsPublic)
    {
        return "public";
    }
    else
    {
        return "private";
    }
}

Tato metoda (se dvěma přetíženími) umožňuje zahrnutí dodatečných informací do metadat buď na úrovni člena nebo typu. Je možné zahrnout rady o tom, zda je člen veřejný nebo soukromý, a komentáře, které by se zachovaly v průběhu exportu a importu schématu. Tyto informace by byly ztraceny bez této metody. Tato metoda nezpůsobí vkládání nebo odstraňování členů nebo typů, ale spíše přidává další data do schémat na některé z těchto úrovní.

Metoda je přetížená a může mít buď Type (clrtype parametr), nebo MemberInfo (memberInfo parametr). Druhý parametr je vždy a Type (dataContractType parametr). Tato metoda se volá pro každý člen a typ náhradního dataContractType typu.

Některé z těchto přetížení musí vrátit buď null nebo serializovatelný objekt. Objekt, který není null, bude serializován jako poznámka do exportovaného schématu. Type Pro přetížení se každý typ exportovaný do schématu odešle do této metody v prvním parametru spolu s náhradním typem jako dataContractType parametr. MemberInfo Pro přetížení každý člen, který je exportován do schématu, odešle své informace jako memberInfo parametr s náhradním typem v druhém parametru.

GetCustomDataToExport – metoda (Type, Type)

Metoda IDataContractSurrogate.GetCustomDataToExport(Type, Type) se volá během exportu schématu pro každou definici typu. Metoda při exportu přidá informace k typům v rámci schématu. Každému definovanému typu se do této metody odešle, aby bylo možné určit, jestli do schématu musí být zahrnuta nějaká další data.

GetCustomDataToExport – metoda (MemberInfo, Type)

Volá se IDataContractSurrogate.GetCustomDataToExport(MemberInfo, Type) během exportu pro každého člena v typech, které jsou exportovány. Tato funkce umožňuje přizpůsobit všechny komentáře pro členy, které budou zahrnuty do schématu při exportu. Do této metody se odesílají informace pro každého člena v rámci třídy, aby bylo možné zkontrolovat, jestli je potřeba do schématu přidat nějaká další data.

Výše uvedený příklad hledá dataContractType pro každého člena náhradního. Potom vrátí příslušný modifikátor přístupu pro každé pole. Bez tohoto přizpůsobení je výchozí hodnota modifikátorů přístupu veřejná. Proto by všechny členy byly definovány jako veřejné v kódu generovaném pomocí exportovaného schématu bez ohledu na to, jaká jsou jejich skutečná omezení přístupu. Pokud tuto implementaci nepoužíváte, člen by byl ve exportovaném schématu veřejný, numpens i když byl definován v náhradním souboru jako soukromý. Pomocí této metody lze v exportovaném schématu vygenerovat modifikátor přístupu jako soukromý.

GetReferencedTypeOnImport – metoda

Tato metoda mapuje Type náhradní na původní typ. Tato metoda je volitelná pro import schématu.

Při vytváření náhrady, která importuje schéma a generuje kód pro něj, je dalším úkolem definovat typ náhradní instance do původního typu.

Pokud vygenerovaný kód potřebuje odkazovat na existující typ uživatele, provede se to implementací GetReferencedTypeOnImport metody.

Při importu schématu se tato metoda volá pro každou deklaraci typu k mapování náhradního datového kontraktu na typ. Parametry řetězce typeName a typeNamespace definujte název a obor názvů náhradního typu. Vrácená hodnota GetReferencedTypeOnImport slouží k určení, zda je potřeba vygenerovat nový typ. Tato metoda musí vrátit platný typ nebo hodnotu null. Pro platné typy se vrácený typ použije jako odkazovaný typ v generovaném kódu. Pokud je vrácena hodnota null, nebude odkazován žádný typ a nový typ musí být vytvořen. Existuje-li několik náhradních dotazů, je možné provést mapování pro každý náhradní typ zpět na jeho počáteční typ.

Parametr customData je objekt původně vrácený z GetCustomDataToExport. To customData se používá, když náhradní autoři chtějí do metadat vložit další data nebo rady, které se mají použít při importu ke generování kódu.

ProcessImportedType – metoda

Metoda ProcessImportedType přizpůsobí jakýkoli typ vytvořený z importu schématu. Tato metoda je nepovinná.

Při importu schématu tato metoda umožňuje přizpůsobit všechny importované informace o typu a kompilaci. Příklad:

public System.CodeDom.CodeTypeDeclaration ProcessImportedType(System.CodeDom.CodeTypeDeclaration typeDeclaration, System.CodeDom.CodeCompileUnit compileUnit)
{
    Console.WriteLine("ProcessImportedType");
    foreach (CodeTypeMember member in typeDeclaration.Members)
    {
        object memberCustomData = member.UserData[typeof(IDataContractSurrogate)];
        if (memberCustomData != null
          && memberCustomData is string
          && ((string)memberCustomData == "private"))
        {
            member.Attributes = ((member.Attributes & ~MemberAttributes.AccessMask) | MemberAttributes.Private);
        }
    }
    return typeDeclaration;
}

Během importu se tato metoda volá pro každý vygenerovaný typ. Změňte zadanou CodeTypeDeclaration nebo upravenou hodnotu CodeCompileUnit. To zahrnuje změnu názvu, členů, atributů a mnoha dalších vlastností objektu CodeTypeDeclaration. Zpracováním CodeCompileUnitje možné upravit direktivy, obory názvů, odkazovaná sestavení a několik dalších aspektů.

Parametr CodeTypeDeclaration obsahuje deklaraci typu DOM kódu. Parametr CodeCompileUnit umožňuje úpravy pro zpracování kódu. Vrácení null výsledků v deklaraci typu, která se zahodí. Naopak při vrácení znaku CodeTypeDeclarationse změny zachovají.

Pokud se vlastní data vloží během exportu metadat, je potřeba je uživateli poskytnout během importu, aby je bylo možné použít. Tato vlastní data se dají použít pro rady programovacího modelu nebo jiné komentáře. Každá CodeTypeDeclaration instance CodeTypeMember obsahuje vlastní data jako UserData vlastnost přetypování na IDataContractSurrogate typ.

Výše uvedený příklad provede některé změny v importovaném schématu. Kód zachovává soukromé členy původního typu pomocí náhradního typu. Výchozí modifikátor přístupu při importu schématu je public. Proto budou všichni členové náhradního schématu veřejné, pokud nebudou upraveni, jako v tomto příkladu. Během exportu se vlastní data vloží do metadat o tom, které členy jsou soukromé. Příklad vyhledá vlastní data, zkontroluje, jestli je modifikátor přístupu soukromý, a pak upraví příslušný člen tak, aby byl soukromý nastavením jeho atributů. Bez tohoto přizpůsobení numpens by byl člen definován jako veřejný místo soukromého.

GetKnownCustomDataTypes – metoda

Tato metoda získá vlastní datové typy definované ze schématu. Metoda je volitelná pro import schématu.

Metoda se volá na začátku exportu a importu schématu. Metoda vrátí vlastní datové typy použité v exportovaném nebo importovaném schématu. Metoda se předává Collection<T> ( customDataTypes parametr), což je kolekce typů. Metoda by měla do této kolekce přidat další známé typy. Známé vlastní datové typy jsou potřeba k povolení serializace a deserializace vlastních dat pomocí DataContractSerializer. Další informace naleznete v tématu Známé typy kontraktů dat.

Implementace náhradního nasazení

Pokud chcete použít náhradní kontrakt dat v rámci WCF, musíte postupovat podle několika speciálních postupů.

Použití náhrady pro serializaci a deserializaci

DataContractSerializer Slouží k provádění serializace a deserializace dat s náhradním. Vytvoří se DataContractSerializer v souboru DataContractSerializerOperationBehavior. Je nutné zadat i náhradní náhradu.

Implementace serializace a deserializace
  1. Vytvořte instanci ServiceHost služby. Úplné pokyny naleznete v tématu Základní programování WCF.

  2. Pro každého ServiceEndpoint zadaného hostitele služby vyhledejte jeho OperationDescription.

  3. Prohledejte chování operace a zjistěte, jestli je nalezena instance DataContractSerializerOperationBehavior .

  4. DataContractSerializerOperationBehavior Pokud je nalezen, nastavte jeho DataContractSurrogate vlastnost na novou instanci náhradní. Pokud se žádná DataContractSerializerOperationBehavior nenajde, vytvořte novou instanci a nastavte DataContractSurrogate člena nového chování na novou instanci náhradní instance.

  5. Nakonec toto nové chování přidejte do aktuálního chování operace, jak je znázorněno v následujícím příkladu:

    using (ServiceHost serviceHost = new ServiceHost(typeof(InventoryCheck)))
        foreach (ServiceEndpoint ep in serviceHost.Description.Endpoints)
        {
            foreach (OperationDescription op in ep.Contract.Operations)
            {
                DataContractSerializerOperationBehavior dataContractBehavior =
                    op.Behaviors.Find<DataContractSerializerOperationBehavior>()
                    as DataContractSerializerOperationBehavior;
                if (dataContractBehavior != null)
                {
                    dataContractBehavior.DataContractSurrogate = new InventorySurrogated();
                }
                else
                {
                    dataContractBehavior = new DataContractSerializerOperationBehavior(op);
                    dataContractBehavior.DataContractSurrogate = new InventorySurrogated();
                    op.Behaviors.Add(dataContractBehavior);
                }
            }
        }
    

Použití náhradního příkazu pro import metadat

Při importu metadat, jako je WSDL a XSD k vygenerování kódu na straně klienta, je potřeba náhradní kód přidat do komponenty zodpovědné za generování kódu ze schématu XSD. XsdDataContractImporter Uděláte to tak, že přímo upravíte WsdlImporter použité k importu metadat.

Implementace náhrady pro dovoz metadat
  1. Importujte metadata pomocí WsdlImporter třídy.

  2. TryGetValue Pomocí této metody zkontrolujte, jestli XsdDataContractImporter byla definována.

  3. Pokud metoda TryGetValue vrátí false, vytvořte novou XsdDataContractImporter a nastavte její Options vlastnost na novou instanci ImportOptions třídy. V opačném případě použijte dovozce vrácený parametrem outTryGetValue metody.

  4. Pokud není XsdDataContractImporter definováno ImportOptions , nastavte vlastnost na novou instanci ImportOptions třídy.

  5. DataContractSurrogate Nastavte vlastnost ImportOptionsXsdDataContractImporter na novou instanci náhradní instance.

  6. XsdDataContractImporter Přidejte do kolekce vrácené State vlastností WsdlImporter (zděděno z MetadataExporter třídy.)

  7. Použijte metodu ImportAllContracts importu všech datových WsdlImporter kontraktů v rámci schématu. V posledním kroku se kód vygeneruje ze schémat načtených voláním do náhradního kódu.

    MetadataExchangeClient mexClient = new MetadataExchangeClient(metadataAddress);
    mexClient.ResolveMetadataReferences = true;
    MetadataSet metaDocs = mexClient.GetMetadata();
    WsdlImporter importer = new WsdlImporter(metaDocs);
    object dataContractImporter;
    XsdDataContractImporter xsdInventoryImporter;
    if (!importer.State.TryGetValue(typeof(XsdDataContractImporter),
        out dataContractImporter))
        xsdInventoryImporter = new XsdDataContractImporter();
    
    xsdInventoryImporter = (XsdDataContractImporter)dataContractImporter;
    xsdInventoryImporter.Options ??= new ImportOptions();
    xsdInventoryImporter.Options.DataContractSurrogate = new InventorySurrogated();
    importer.State.Add(typeof(XsdDataContractImporter), xsdInventoryImporter);
    
    Collection<ContractDescription> contracts = importer.ImportAllContracts();
    

Použití náhradního příkazu pro export metadat

Ve výchozím nastavení je potřeba při exportu metadat z WCF pro službu vygenerovat schéma WSDL i XSD. Náhradní náhradu je potřeba přidat do komponenty zodpovědné za generování schématu XSD pro typy kontraktů dat. XsdDataContractExporter Uděláte to tak, že buď použijete chování, které implementuje IWsdlExportExtension úpravy WsdlExporter, nebo přímo upravit WsdlExporter použité k exportu metadat.

Použití náhradních náhrad pro export metadat
  1. Vytvořte nový WsdlExporter nebo použijte wsdlExporter parametr předaný metodě ExportContract .

  2. TryGetValue Pomocí funkce zkontrolujte, jestli XsdDataContractExporter byla definována.

  3. Pokud TryGetValue se vrátí false, vytvořte nový XsdDataContractExporter s vygenerovanými schématy XML z objektu WsdlExporter, a přidejte jej do kolekce vrácené State vlastností WsdlExporter. V opačném případě použijte vývozce vrácený out parametrem TryGetValue metody.

  4. Pokud není XsdDataContractExporter definováno ExportOptions , nastavte Options vlastnost na novou instanci ExportOptions třídy.

  5. DataContractSurrogate Nastavte vlastnost ExportOptionsXsdDataContractExporter na novou instanci náhradní instance. Následné kroky pro export metadat nevyžadují žádné změny.

    WsdlExporter exporter = new WsdlExporter();
    //or
    //public void ExportContract(WsdlExporter exporter,
    // WsdlContractConversionContext context) { ... }
    object dataContractExporter;
    XsdDataContractExporter xsdInventoryExporter;
    if (!exporter.State.TryGetValue(typeof(XsdDataContractExporter),
        out dataContractExporter))
    {
        xsdInventoryExporter = new XsdDataContractExporter(exporter.GeneratedXmlSchemas);
    }
    else
    {
        xsdInventoryExporter = (XsdDataContractExporter)dataContractExporter;
    }
    
    exporter.State.Add(typeof(XsdDataContractExporter), xsdInventoryExporter);
    
    if (xsdInventoryExporter.Options == null)
        xsdInventoryExporter.Options = new ExportOptions();
    xsdInventoryExporter.Options.DataContractSurrogate = new InventorySurrogated();
    

Viz také