Tipi conosciuti di contratto dati

La classe KnownTypeAttribute consente di specificare, in anticipo, i tipi che devono essere presi in considerazione durante la deserializzazione. Per un esempio pratico, vedere Known Types.

In genere, quando si passano parametri e valori restituiti tra un client e un servizio, entrambi gli endpoint condividono tutti i contratti dati dei dati da trasmettere. Nelle circostanze seguenti, tuttavia, la situazione è diversa:

  • Il contratto dati inviato deriva dal contratto dati previsto. Per ulteriori informazioni, vedere la sezione sull'ereditarietà in Equivalenza dei contratti dati. In tale caso, i dati trasmessi non hanno lo stesso contratto dati previsto dall'endpoint di destinazione.
  • Il tipo dichiarato per le informazioni da trasmettere è un'interfaccia, anziché una classe, una struttura o un'enumerazione. Non è pertanto possibile conoscere in anticipo quale tipo che implementa l'interfaccia viene effettivamente inviato e, di conseguenza, l'endpoint di destinazione non è in grado di determinare, in anticipo, il contratto dati per i dati trasmessi.
  • Il tipo dichiarato per le informazioni da trasmettere è Object. Dato che ogni tipo eredita da Object e che non è possibile sapere in anticipo qual è il tipo effettivamente inviato, l'endpoint di destinazione non è in grado di determinare in anticipo il contratto dati per i dati trasmessi. Questo è un caso speciale del primo elemento: ogni contratto dati deriva dall'impostazione predefinita, un contratto dati vuoto generato per Object.
  • Alcuni tipi, tra cui i tipi .NET Framework, dispongono di membri che rientrano in una delle tre categorie precedenti. Hashtable, ad esempio, utilizza Object per memorizzare gli oggetti effettivi nella tabella hash. Durante la serializzazione di questi tipi, il lato di destinazione non è in grado di determinare in anticipo il contratto dati per questi membri.

Classe KnownTypeAttribute

Quando i dati arrivano a un endpoint di destinazione, il runtime di WCF tenta di deserializzarli in un'istanza di un tipo Common Language Runtime (CLR). Il tipo di cui viene creata l'istanza per la deserializzazione viene scelto controllando innanzitutto il messaggio in arrivo per determinare il contratto dati al quale è conforme il contenuto del messaggio. Il motore di deserializzazione tenta quindi di trovare un tipo CLR che implementi un contratto dati compatibile con il contenuto del messaggio. L'insieme di tipi di candidato consentiti dal motore di deserializzazione durante questo processo viene chiamato insieme di "tipi conosciuti" del deserializzatore.

Un modo per consentire al motore di deserializzazione di conoscere un tipo consiste nell'utilizzare KnownTypeAttribute. L'attributo non può essere applicato a membri dati singoli, ma solo a tutti i tipi di contratto dati. L'attributo viene applicato a un tipo esterno che può essere una classe o una struttura. Nell'utilizzo più elementare, l'applicazione dell'attributo specifica un tipo come "tipo conosciuto". Ciò fa sì che il tipo conosciuto sia parte di questo insieme di tipi conosciuti ogni volta che viene deserializzato un oggetto del tipo esterno o un qualsiasi oggetto a cui si faccia riferimento tramite i suoi membri. Allo stesso tipo è possibile applicare più di un attributo KnownTypeAttribute.

Tipi conosciuti e primitivi

I tipi primitivi, così come certi tipi trattati come primitivi (ad esempio, DateTime e XmlElement) sono sempre "conosciuti" e non è mai necessario aggiungerli tramite questo meccanismo. Le matrici di tipi primitivi, tuttavia, devono essere aggiunte in modo esplicito. La maggior parte degli insiemi sono considerati equivalenti alle matrici. (Gli insiemi non generici sono considerati equivalenti alle matrici di Object). Per un esempio di utilizzo di primitivi, matrici di primitivi e insiemi di primitivi, vedere l'esempio 4.

Nota

A differenza di altri tipi di primitivi, la struttura DateTimeOffset non è un tipo conosciuto per impostazione predefinita, pertanto deve essere aggiunta manualmente all'elenco dei tipi conosciuti.

Esempi

Negli esempi seguenti viene illustrata la classe KnownTypeAttribute utilizzata.

Esempio 1

Esistono tre classi con una relazione di ereditarietà.

La classe CompanyLogo seguente può essere serializzata, ma non può essere deserializzata se il membro ShapeOfLogo è impostato su un CircleType o su un oggetto TriangleType, perché il motore di deserializzazione non riconosce alcun tipo con i nomi del contratto dati "Circle" o "Triangle".

La modalità corretta per scrivere il tipo CompanyLogo è illustrata nel codice seguente.

Ogni volta che il tipo esterno CompanyLogo2 viene deserializzato, il motore di deserializzazione viene a conoscenza di CircleType e TriangleType e, pertanto, è in grado di cercare i tipi corrispondenti per i contratti dati "Circle" e "Triangle".

Esempio 2

Nell'esempio seguente, anche se sia CustomerTypeA che CustomerTypeB hanno il contratto dati Customer, viene creata un'istanza di CustomerTypeB ogni volta che viene deserializzato un PurchaseOrder, perché solo CustomerTypeB è conosciuto al motore di deserializzazione.

Esempio 3

Nell'esempio seguente, un Hashtable memorizza internamente il contenuto come Object. Per deserializzare correttamente una tabella hash, il motore di deserializzazione deve conoscere l'insieme dei tipi possibili che possono verificarsi. In questo caso, si sa in anticipo che solo gli oggetti Book e Magazine vengono memorizzati in Catalog, pertanto vengono aggiunti utilizzando l'attributo KnownTypeAttribute.

Esempio 4

Nell'esempio seguente, un contratto dati memorizza un numero e un'operazione da eseguire sul numero. Il membro dati Numbers può essere un numero intero, una matrice di numeri interi o un List che contiene numeri interi.

Questo è il codice dell'applicazione.

Tipi conosciuti, ereditarietà e interfacce

Quando un tipo conosciuto è associato a un particolare tipo utilizzando l'attributo KnownTypeAttribute, il tipo conosciuto viene associato anche a tutti i tipi derivati di quel tipo. Si consideri, ad esempio, il codice seguente.

La classe DoubleDrawing non richiede l'attributo KnownTypeAttribute per utilizzare Square e Circle nel campo AdditionalShape, perché nella classe di base (Drawing) questi attributi sono già applicati.

I tipi conosciuti possono essere associati solo a classi e strutture, non a interfacce.

Tipi conosciuti che utilizzano metodi generici aperti

Potrebbe essere necessario aggiungere un tipo generico come tipo conosciuto. Non è tuttavia possibile passare un tipo generico aperto come parametro all'attributo KnownTypeAttribute.

Questo problema può essere risolto utilizzando un meccanismo alternativo, ovvero scrivere un metodo che restituisca un elenco di tipi da aggiungere all'insieme di tipi conosciuti. Specificare quindi il nome del metodo come argomento di tipo stringa per l'attributo KnownTypeAttribute, per far fronte ad alcune restrizioni.

Il metodo deve esistere nel tipo al quale è applicato l'attributo KnownTypeAttribute, deve essere statico, non deve accettare parametri e deve restituire un oggetto che possa essere assegnato a IEnumerable di Type.

Non è possibile combinare l'attributo KnownTypeAttribute con un nome di metodo e attributi KnownTypeAttribute con i tipi effettivi sullo stesso tipo. Non è inoltre possibile applicare più di un KnownTypeAttribute con un nome di metodo allo stesso tipo.

Fare riferimento alla classe seguente.

Il campo theDrawing contiene istanze di una classe ColorDrawing generica e una classe BlackAndWhiteDrawinggenerica, che ereditano entrambe da una classe Drawing generica. In genere, è necessario aggiungerle entrambe a tipi conosciuti, ma quella che segue non è una sintassi valida per gli attributi.

// Invalid syntax for attributes:
// [KnownType(typeof(ColorDrawing<T>))]
// [KnownType(typeof(BlackAndWhiteDrawing<T>))]
' Invalid syntax for attributes:
' <KnownType(GetType(ColorDrawing(Of T))), _
' KnownType(GetType(BlackAndWhiteDrawing(Of T)))>

Per restituire questi tipi, è pertanto necessario creare un metodo. Nel codice seguente viene illustrato il modo corretto per scrivere questo tipo.

Altri modi per aggiungere tipi conosciuti.

È inoltre possibile aggiungere tipi a ReadOnlyCollection, a cui si accede tramite la proprietà KnownTypes di DataContractSerializer.

I tipi conosciuti possono essere aggiunti anche tramite un file di configurazione. Ciò è utile quando non si controlla il tipo che richiede tipi conosciuti per una deserializzazione corretta, ad esempio quando si utilizzano librerie dei tipi di terze parti con Windows Communication Foundation (WCF).

Nel file di configurazione seguente viene illustrato come specificare un tipo conosciuto in un file di configurazione.

<configuration>

<system.runtime.serialization>

<dataContractSerializer>

<declaredTypes>

<add type="MyCompany.Library.Shape,

MyAssembly, Version=2.0.0.0, Culture=neutral,

PublicKeyToken=XXXXXX, processorArchitecture=MSIL">

<knownType type="MyCompany.Library.Circle,

MyAssembly, Version=2.0.0.0, Culture=neutral,

PublicKeyToken=XXXXXX, processorArchitecture=MSIL"/>

</add>

</declaredTypes>

</dataContractSerializer>

</system.runtime.serialization>

</configuration>

Nel file di configurazione precedente è stato dichiarato che un tipo di contratto dati chiamato MyCompany.Library.Shape ha MyCompany.Library.Circle come tipo conosciuto.

Vedere anche

Riferimenti

KnownTypeAttribute
Hashtable
Object
DataContractSerializer
KnownTypes

Concetti

Equivalenza dei contratti dati

Altre risorse

Known Types