2010-02-24 11 views
15

ho queste classi:WCF - probem con la serializzazione di tipi ereditati

[DataContract] 
public class ErrorBase {} 

[DataContract] 
public class FileMissingError: ErrorBase {} 

[DataContract] 
public class ResponseFileInquiry 
{ 
    [DataMember] 
    public List<ErrorBase> errors {get;set;}; 
} 

Un'istanza della classe ResponseFileInquiry è ciò che il mio servizio metodo restituisce al client. Ora, se io riempio ResponseFileInquiry.errors con le istanze di ErrorBase, tutto funziona bene, ma se aggiungo un'istanza di tipo ereditario FileMissingError, ottengo un'eccezione lato servizio durante la serializzazione:

Type 'MyNamespace.FileMissingError' with data contract name 'FileMissingError' 
is not expected. Add any types not known statically to the list of known types - 
for example, by using the KnownTypeAttribute attribute or by adding them to the 
list of known types passed to DataContractSerializer.' 

Così serializer sta ottenendo confuso perché si aspetta che l'Elenco contenga gli oggetti di tipo dichiarato (ErrorBase) ma riceve oggetti di tipo ereditato (FileMissingError).

Ho un sacco di tipi di errore e l'elenco conterrà combinazioni di essi, quindi cosa posso fare per farlo funzionare ???

risposta

17

Si dovrebbe aggiungere l'attributo KnownType alla classe base

[DataContract] 
[KnownType(typeof(FileMissingError))] 
public class ErrorBase {} 

Per saperne di più attributo KnownType in questo blog

+1

Grazie, il blog ha tutti i modi possibili per dichiarare i tipi conosciuti. – Andrey

+0

Nel mio caso, l'utilizzo di KnownType non aiuta molto poiché il tipo proviene da un assembly separato a cui non faccio riferimento. Altri sviluppatori stanno estendendo la classe che ho per il mio DataContract per aggiungere alcune proprietà. Cosa succede se volessi semplicemente buttare via qualsiasi classe derivata e usare la classe base? – brendonparker

+0

Questa è la soluzione che ho dovuto usare: http: // StackOverflow.com/a/8414390/2460073 Nota: ho dovuto applicare il cliente DataContractSerializer al ClientBase – brendonparker

7

Prova questo:

[DataContract] 
[KnownType(typeof(FileMissingError))] 
public class ErrorBase {} 

Come afferma il messaggio di errore, tutte le informazioni che non possono essere conosce in modo statico (come il rapporto polimorfica avete espresso qui) deve essere alimentato tramite attributi. In questo caso è necessario specificare che il contratto dati FileMissingError è un tipo noto della sua classe base, ErrorBase.

+0

Ciò significa che è necessario specificare tutte le classi di errori figlio qui? C'è qualche altro modo per farlo? Non mi piace il fatto che la classe genitore sia a conoscenza delle classi figlie in un modo di attriibutes allegati e di clausole "using" nel file di classe. L'eccezione diceva "Aggiungi qualsiasi tipo non noto staticamente all'elenco dei tipi noti, ad esempio utilizzando l'attributo KnownTypeAttribute o aggiungendoli all'elenco dei tipi noti passati a DataContractSerializer." Quindi c'è un modo per aggiungerli a un elenco di tipi noti? Come lo faccio? – Andrey

+2

È possibile passare l'elenco dei tipi noti al serializzato solo se si esegue manualmente la serializzazione dei contratti. Poiché sembra che tu stia consentendo a WCF di gestire la serializzazione per te, l'unica cosa che puoi fare è aggiungere un 'KnownTypeAttribute' alla classe base, una per ogni classe figlio di cui ha bisogno di essere informata. –

2

un filino in ritardo, ma forse per le generazioni future. =)

Se non si desidera aggiungere un attributo per ogni classe bambino alla classe genitore, si potrebbe costruire un elenco dei tipi conosciuti nel costruttore statico classi genitore utilizzando

IEnumerable<Assembly> assemblies = AppDomain.CurrentDomain 
              .GetAssemblies() 
              .Where(a => !a.GlobalAssemblyCache); 

IEnumerable<Type> serializableTypes = assemblies.SelectMany(a => a.GetTypes()) 
               .Where(t => IsSerializable(t)); 

// ... 

private static bool IsSerializable(Type type) 
{ 
    return type.GetCustomAttributes(true).Any(a => a is DataContractAttribute); 
} 

e passare questo elenca il costruttore de/serializzatori. Non so quanto sia solida questa soluzione, ma è quello che sto facendo e finora funziona. È un po 'lento, quindi assicurati di memorizzare nella cache il risultato.

Problemi correlati