2010-02-24 22 views
15

que tienen estas clases:WCF - problema tomando con la serialización de tipos heredados

[DataContract] 
public class ErrorBase {} 

[DataContract] 
public class FileMissingError: ErrorBase {} 

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

Una instancia de la clase ResponseFileInquiry es lo que mi método de servicio devuelve al cliente. Ahora, si completo ResponseFileInquiry.errors con instancias de ErrorBase, todo funciona bien, pero si añado una instancia de tipo hereditario FileMissingError, recibo una excepción lado del servicio durante la serialización:

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.' 

Así serializador se está confundido porque está esperando que la Lista contenga los objetos de tipo declarados (ErrorBase) pero obtiene objetos de tipo heredado (FileMissingError).

Tengo todo el montón de tipos de error y la lista contendrá combinaciones de ellos, así que ¿qué puedo hacer para que funcione?

Respuesta

17

Usted debe agregar el atributo KnownType a su clase base

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

Leer más acerca de atributos KnownType en este blog

+1

Gracias, la entrada del blog tiene todas las formas posibles de declarar tipos conocidos. – Andrey

+0

En mi caso, usar KnownType no ayuda mucho ya que el tipo proviene de un ensamblaje separado al que no hago referencia. Otros desarrolladores están ampliando la clase que tengo para mi DataContract para agregar algunas propiedades. ¿Qué pasa si solo quisiera descartar cualquier clase derivada y usar la clase base? – brendonparker

+0

Esta es la solución que tuve que usar: http: // stackoverflow.com/a/8414390/2460073 Nota: tuve que aplicar el cliente DataContractSerializer a ClientBase – brendonparker

7

Prueba esto:

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

Como dice el mensaje de error, cualquier información que no se puede saber de forma estática (como la relación polimórfico que ha expresado aquí) deben ser suministrados a través de atributos. En este caso, debe especificar que su contrato de datos FileMissingError es un tipo conocido de su clase base, ErrorBase.

+0

¿Entonces eso significa que necesito especificar todas las clases de error secundarias aquí? ¿Hay alguna otra forma de hacerlo? No me gusta el hecho de que la clase de padres esté al tanto de las clases de niños en una forma de atributos adjuntos y cláusulas de "uso" en el archivo de la clase. La excepción dice "Agregue cualquier tipo no conocido estáticamente a la lista de tipos conocidos, por ejemplo, utilizando el atributo KnownTypeAttribute o agregándolos a la lista de tipos conocidos pasados ​​a DataContractSerializer". Entonces, ¿hay alguna manera de simplemente agregarlos a una lista de tipos conocidos? ¿Cómo puedo hacer eso? – Andrey

+2

Puede pasar la lista de tipos conocidos a la serialización solo si usted mismo está serializando manualmente los contratos. Dado que parece que está permitiendo que WCF maneje la serialización por usted, lo único que puede hacer es agregar un 'KnownTypeAttribute' a la clase base, uno para cada clase secundaria que necesita conocer. –

2

Un poco poco de retraso, pero a lo mejor para las generaciones futuras. =)

Si no desea añadir un atributo para cada clase hija a su clase padre, usted podría construir una lista de tipos conocidos en la clases padre constructor estático utilizando

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

y pasar esta lista al constructor de/serializadores. No sé cuán robusta es esta solución, pero eso es lo que estoy haciendo y hasta ahora funciona. Es un poco lento, así que asegúrese de almacenar en caché el resultado.

Cuestiones relacionadas