Pregunta

¿Cómo voy a decirle al servicio WCF lo KnownTypes empleados para la transmisión de datos de vuelta al cliente?

Yo sé que puedo utilizar el atributo [ServiceKnownType], lo que hace la llamada de servicio fina ejecución de un servidor de prueba WCF, sin embargo, todavía no desde el cliente. Me estoy perdiendo algo aquí?

[OperationContract]
[ServiceKnownType(typeof(SubClassA))]
[ServiceKnownType(typeof(SubClassB))]
BaseClassZ GetObject();

Mensaje de error del cliente es:

  

{ "Elemento   'Http://schemas.datacontract.org/2004/07/BaseClassZ'   contiene los datos de un tipo que se asigna a   el nombre   'Http://schemas.datacontract.org/2004/07/SubClassA'.   El deserializer no tiene conocimiento de   cualquier tipo que se asigna a este nombre.   Considere el uso de un DataContractResolver   o añadir el tipo correspondiente a   'SubClassA' a la lista de tipos conocidos   - por ejemplo, mediante el atributo KnownTypeAttribute o por   de añadir a la lista de tipos conocidos   pasó a DataContractSerializer. "}

Serialización / deserializar el objeto en el servidor WCF utilizando un DataContractSerializer y una lista de KnownTypes bien funciona.

ACTUALIZACIÓN: Parece que puedo conseguir que el cliente leer el objeto correctamente si añado KnownType atribuye a la clase base, pero todavía estoy buscando una forma de evitar esto si es posible ya que la clase base se utiliza para una gran cantidad de artículos y no quieren tener que modificar los atributos KnownType en la clase base en cualquier momento puedo añadir un nuevo elemento.

[DataContract]
[KnownType(typeof(SubClassA))]
[KnownType(typeof(SubClassB))]
public class BaseClassZ 
{
    ...
}
¿Fue útil?

Solución

Para evitar disuadir el código de servicio puso a los tipos conocidos en web.config del servicio:

<system.runtime.serialization>
    <dataContractSerializer>
        <declaredTypes>
            <add type="SomeNs.BaseClassZ, SomeAssembly">
                <knownType type="SomeNs.SubClassA, SomeAssembly" />
                <knownType type="SomeNs.SubClassB, SomeAssembly" />
            </add>
        </declaredTypes>
    </dataContractSerializer>
</system.runtime.serialization>

Si desea hacerlo por código es necesario utilizar este atributo en la interfaz de servicio y no en el método de operación, pero yo preferiría el enfoque declarativo:

[ServiceContract]
[ServiceKnownType(typeof(SubClassA))]
[ServiceKnownType(typeof(SubClassB))]
public interface IFoo
{
    [OperationContract]
    BaseClassZ GetObject();
}

ACTUALIZACIÓN:

He puesto en marcha un proyecto muestra ilustra el uso de web.config para tipos de configure conocido que es mi enfoque preferido. Y otro proyecto de ejemplo demostrando el segundo enfoque.


ACTUALIZACIÓN 2:

Después de mirar el código actualizado con el cliente aplicación de Silverlight notamos la siguiente definición:

[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(ConfigurationName="ServiceReference1.IService1")]
public interface IService1 {

    [System.ServiceModel.OperationContractAttribute(AsyncPattern=true, Action="http://tempuri.org/IService1/GetMany", ReplyAction="http://tempuri.org/IService1/GetManyResponse")]
    System.IAsyncResult BeginGetMany(System.AsyncCallback callback, object asyncState);

    System.Collections.ObjectModel.ObservableCollection<MyCommonLib.BaseClassZ> EndGetMany(System.IAsyncResult result);

    [System.ServiceModel.OperationContractAttribute(AsyncPattern=true, Action="http://tempuri.org/IService1/GetSingle", ReplyAction="http://tempuri.org/IService1/GetSingleResponse")]
    [System.ServiceModel.ServiceKnownTypeAttribute(typeof(MyCommonLib.SubClassA))]
    [System.ServiceModel.ServiceKnownTypeAttribute(typeof(MyCommonLib.SubClassB))]
    System.IAsyncResult BeginGetSingle(System.AsyncCallback callback, object asyncState);

    MyCommonLib.BaseClassZ EndGetSingle(System.IAsyncResult result);
}

Aviso cómo el método BeginGetSingle contiene el tipo conocido atributos mientras que el método BeginGetMany no lo hace. De hecho esos atributos deben ser colocados en la definición del servicio de manera que las miradas clase como esta.

[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(ConfigurationName="ServiceReference1.IService1")]
[System.ServiceModel.ServiceKnownTypeAttribute(typeof(MyCommonLib.SubClassA))]
[System.ServiceModel.ServiceKnownTypeAttribute(typeof(MyCommonLib.SubClassB))]
public interface IService1
{
    [System.ServiceModel.OperationContractAttribute(AsyncPattern=true, Action="http://tempuri.org/IService1/GetMany", ReplyAction="http://tempuri.org/IService1/GetManyResponse")]
    System.IAsyncResult BeginGetMany(System.AsyncCallback callback, object asyncState);

    System.Collections.ObjectModel.ObservableCollection<MyCommonLib.BaseClassZ> EndGetMany(System.IAsyncResult result);

    [System.ServiceModel.OperationContractAttribute(AsyncPattern=true, Action="http://tempuri.org/IService1/GetSingle", ReplyAction="http://tempuri.org/IService1/GetSingleResponse")]
    System.IAsyncResult BeginGetSingle(System.AsyncCallback callback, object asyncState);

    MyCommonLib.BaseClassZ EndGetSingle(System.IAsyncResult result);
}

Como se trata de una clase autogenerada que podría haber un error en el SLsvcUtil.exe y svcutil.exe ya que exhibe el mismo comportamiento. Poner los atributos de texto conocidos en su lugar correcto resuelve el problema. El problema es que esta clase se genera automáticamente por una herramienta y si intenta regenerarlo a partir del WSDL que se hace un lío de nuevo.

Así que parece que si tiene la siguiente definición de servicio:

[ServiceContract]
[ServiceKnownType(typeof(SubClassA))]
[ServiceKnownType(typeof(SubClassB))]
public interface IService1
{
    [OperationContract]
    BaseClassZ[] GetMany();

    [OperationContract]
    BaseClassZ GetSingle();
}

Y los contratos 3 de datos utilizados aquí son compartidas entre el cliente y el servidor al importar la definición del servicio, el método que devuelve una colección no recibe los atributos del tipo conocido correcto en el proxy cliente generado. Tal vez esto es por diseño.

Otros consejos

Me pasaba horas de hoy en lo que, lo mejor que puedo decir, es el mismo problema exacto. La solución para mí era utilizar el método de la biblioteca AddGenericResolver ServiceModelEx de IDesign.

NOTA: .NET 4.0 requerido, ya que utiliza DataContractResolver

Se puede encontrar en IDesign Descargas página.

Todo lo que tenía que hacer en mi caso era añadir la siguiente línea de código:

Client.AddGenericResolver( typeof ( K2Source ) );

Espero que esto ayude a alguien más por ahí excepto unas pocas horas!

Se puede encontrar más información en el libro "Programación de Servicios WCF: Mastering WCF y el servicio de autobuses Azure AppFabric" por Juval Lowy

Hay otra manera de hacer esto. En lugar de utilizar "referencia de servicio complemento" se codifican las clases de proxy. Es un poco más de codificación al principio, pero le da un aspecto mucho más estable y robusta solución. Hemos encontrado que esto nos permite ahorrar tiempo en el largo plazo.

Ver: http://www.dnrtv.com/default.aspx?showNum= 122

Nota:. Esto sólo funciona si usted tiene el control tanto del servidor y el cliente

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top