2009-09-03 28 views
8

que tienen la siguiente:WCF: Interfaces, Generics y ServiceKnownType

[ServiceContract] 
[ServiceKnownType(typeof(ActionParameters))] 
[ServiceKnownType(typeof(SportProgram))] 
[ServiceKnownType(typeof(ActionResult<SportProgram>))] 
public interface ISportProgramBl 
{ 
    [OperationContract] 
    IActionResult<ISportProgram> Get(IActionParameters parameters); 
} 

Cuando ejecuto el método Get me sale el siguiente error:

There was an error while trying to serialize parameter http://tempuri.org/:GetResult . The InnerException message was 'Type 'PPS.Core.DomainModel.Support.Action.ActionResult`1[ [PPS.Core.DomainModel.SportProgram.ISportProgram, PPS.Core.DomainModel, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]' with data contract name 'ActionResultOfanyType: http://schemas.datacontract.org/2004/07/PPS.Core.DomainModel.Support.Action ' 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.'. Please see InnerException for more details.

De este error Puedo ver que lo que pueda resuelve ActionResult pero no puede resolver ISportProgram aunque tengo ServiceKnownType (typeof (ActionResult < SportProgram>)) en mi interfaz de servicio ...

: Este es el talón de referencia que se genera es similar al siguiente, por lo que se puede ver que los tipos conocidos están siendo llevados a través correctamente:

[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")] 
[System.ServiceModel.ServiceContractAttribute(ConfigurationName="SportProgramStb.ISportProgramBl")] 
public interface ISportProgramBl { 

    [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/ISportProgramBl/Get", ReplyAction="http://tempuri.org/ISportProgramBl/GetResponse")] 
    [System.ServiceModel.ServiceKnownTypeAttribute(typeof(PPS.Core.DomainModel.SportProgram.SportProgram))] 
    [System.ServiceModel.ServiceKnownTypeAttribute(typeof(PPS.Core.DomainModel.Support.Action.ActionParameters))] 
    [System.ServiceModel.ServiceKnownTypeAttribute(typeof(PPS.Core.DomainModel.Support.Action.ActionResult<PPS.Core.DomainModel.SportProgram.SportProgram>))] 
    object Get(object parameters); 
} 

¿Por qué es esto va mal ???? Tenga en cuenta que está recibiendo el servicio WCF correctamente ... pero arroja la excepción cuando se devuelve el resultado.

Por último ActionResult se parece a esto:

public interface IActionResult<T> 
{ 
    T Result { get; set; } 
} 

Saludos Anthony

+0

El tipo 'ActionResult ' que 'Serializer' espera es diferente del tipo' ActionResult 'proporcionado en' ServiceKnownType'! – Lightman

Respuesta

15

Bueno, creo que este es otro caso del "desajuste de impedancia" de SOA vs. OOP. Los dos mundos están bastante separados.

En WCF, todo lo que se está pasando del cliente al servidor se pasa como mensajes serializados - no se utilizan referencias.

Esto significa: todo lo que quieran serializar en el cliente, enviarlo a través del servidor, y deserializar y utilizarlo allí, debe ser concreto - no se puede pasar alrededor de las interfaces, no se puede utilizar "no resuelta "genéricos": tiene que deletrearlo. Básicamente, todo lo que se pasa del cliente a través del cable al servidor debe poder expresarse en un esquema XML.

Esto tiene muchas implicaciones:

  • ninguna interfaz - no se puede pasar alrededor de las interfaces - que necesita para trabajar con tipos concretos
  • ninguna herencia "automático" - no se puede simplemente definir una clase base y pase alrededor de las clases basadas en ella deriva - los que necesitan ser specificied también (eso es lo que el atributo es para ServiceKnownType)
  • no hay genéricos automáticas - una vez más, es necesario utilizar tipos de hormigón en lugar

Esto puede sonar como un montón de restricciones - pero es porque WCF está usando toda la comunicación basada en mensajes - que no puede hacer frente a refereces, herencia, etc. genéricos - es necesario explicarla.

Así que en realidad no tienen una respuesta para usted per se - acabo de pensar que necesita repensar su estrategia y cambiar la forma de su cliente y el servidor intercambian información a través de WCF.

Marc

PS: He hecho un poco más investigación, y contrario a toda mi entendimiento, parece que hay una manera de serializar cualquier cosa que se basa en una interfaz y/o clase base abstracta a través del cable, siempre como se puede estar seguro de que siempre es única .NET en cualquiera de los extremos del cable (es decir, no es interoperable con, por ejemplo Java).

Ver Aaron Skonnard blog post on the NetDataContractSerializer y otro blog post y yet another que muestra cómo utilizar el NetDataContractSerializer para poder pasar alrededor de cosas como IPerson como parámetros a sus métodos.

+0

Estoy dispuesto a darle a WCF todos los consejos que necesita, pero no puedo evitar el uso de las interfaces o los genéricos ... si de alguna manera puedo deshacerme de las interfaces ¿puedo seguir usando genéricos? también es cierto lo contrario si puedo deshacerme de los genéricos, ¿puedo seguir usando interfaces ... lo que significa que es solo la combinación de los 2 los que me están causando problemas? Porque podría crear una versión más específica de IActionResult que no requiera que se utilicen los genéricos ... –

+0

Actualicé mi publicación con información nueva que encontré mientras investigaba este asunto. Espero que esto ayude. –

+0

Gracias por la actualización ... en mi caso, NetDataContractSerializer funciona muy bien ... –

0

¿Está la especificación de las interfaces de los objetos y no tipos concretos?

Editar:

lo que estoy diciendo es que son todos los tipos de hormigón que está pasando en los medicamentos genéricos (incluidos en el objeto secundario a través de interfaces) que se pasa en la lista de tipos conocidos. Hemos tenido problemas de gravelación donde no se conocían todos los tipos.

+0

¿qué es lo que quiere decir con ¿ese? El objeto concreat implementa la interfaz ... – vdhant

0

Usted está devolviendo un IList de T. Tal vez el sistema tenga problemas para descubrir qué es T.

No estoy seguro si está bien devolver una interfaz en lugar de un tipo.

2

Este es uno de los problemas que se resuelven con la ServiceStack.NET - My Open Source .NET y Mono marco de servicios Web.

Service Stack estuvo muy influenciado por Martin Fowlers Data Transfer Object Pattern, ya que le permite simplemente usar DTO para definir sus servicios web, es decir, el modo SOA :).

evito que esta limitación es inherente a WCF mediante la generación de mi propia de WSDL que se comporte como se puede esperar que lo hagan. Como beneficio de reemplazar la configuración compleja de WCF/el modelo de ServiceContract: los servicios web SOAP también funcionan en MONO - see the live demo.

+0

Parece interesante, pero el enlace de demostración en vivo es un 404 –

1

Es una cuestión de edad ya pesar de que la respuesta aceptada es totalmente correcto, yo stumpled sobre esto en la búsqueda de un problema similar y pensé que podría compartir mis experiencias. A menudo es un dolor de cabeza, pero es posible usar genéricos combinados con interfaces con WCF. Aquí está un ejemplo de trabajo de otra aplicación (similar) que he hecho:

[ServiceContract] 
[ServiceKnownType(typeof(CollectionWrapper<IAssociation>))] 
public interface IService : 
{ 
    [OperationContract] 
    ICollectionWrapper<IAssociation> FindAssociation(string name, int pageSize, int page); 
} 

public interface ICollectionWrapper<TModel> 
{ 
    int TotalCount { get; set; } 
    IEnumerable<TModel> Items { get; set; } 
} 

[KnownType(typeof(OrganizationDto))] 
[KnownType(typeof(CompanyDto))] 
public class CollectionWrapper<TModel> : ICollectionWrapper<TModel> 
{ 
    [DataMember] 
    public int TotalCount { get; set; } 
    [DataMember] 
    public IEnumerable<TModel> Items { get; set; } 
} 

public class CompanyDto : IAssociation 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
} 

public class OrganizationDto : IAssociation 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
} 

La clave aquí es utilizar la combinación de KnownType y ServiceKnownType.

Así, en su caso, usted puede hacer algo como esto:

[ServiceContract] 
[ServiceKnownType(typeof(ActionParameters))] 
[ServiceKnownType(typeof(ActionResult<ISportProgram>))] // Actual implementation of container, but interface of generic. 
public interface ISportProgramBl 
{ 
    [OperationContract] 
    IActionResult<ISportProgram> Get(IActionParameters parameters); 
} 

[KnownType(typeof(SportProgram))] // Actual implementation here. 
public class ActionResult<T> 
{ 
    // Other stuff here 
    T FooModel { get; set; } 
} 

Esto funcionará si tiene un contrato compartido (acceso a la interfaz de servicio real) y consumir el contrato con ChannelFactory<ISportProgramBl>. No sé si funciona con una referencia de servicio.

Sin embargo, parece que hay algunos problemas con la puesta en práctica como se ha mencionado aquí:

WCF With a interface and a generic model

Y otra pregunta similar hecha y contestada aquí:

Generic return types with interface type params in WCF