2009-09-03 26 views
8

Ho il seguente:WCF: Interfacce, Generics e ServiceKnownType

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

Quando eseguo il metodo Get ottengo il seguente errore:

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.

Da questo errore posso vedere che si può risolvere ActionResult ma non può risolvere ISportProgram anche se ho ServiceKnownType (typeof (ActionResult < SportProgram>)) sulla mia interfaccia di servizio ...

Nota questo è lo stub di riferimento che viene generato assomiglia a questo, così posso vedere che i tipi noti vengono portati in tutto correttamente:

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

perché è questo che non va ???? Si noti che sta ricevendo correttamente il servizio WCF ... ma lancia l'eccezione quando viene restituito il risultato.

Infine ActionResult assomiglia a questo:

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

Acclamazioni Anthony

+0

Il tipo 'ActionResult ' che 'Serializer' si aspetta è diverso dal tipo' ActionResult 'fornito in' ServiceKnownType'! – Lightman

risposta

15

Bene, penso che questo sia un altro caso di "mancata corrispondenza dell'impedenza" tra SOA e OOP. I due mondo sono abbastanza separati.

In WCF, tutto ciò che viene passato dal client al server viene passato come messaggi serializzati - nessun riferimento viene utilizzato.

Ciò significa: tutto ciò che si vuole serializzare sul client, inviarlo attraverso al server, e deserializzare e usarlo lì, deve essere concreta - non è possibile passare in giro le interfacce, non è possibile utilizzare "non risolto "generici - devi scriverlo. In sostanza, tutto ciò che viene passato dal client sul cavo al server deve essere espressivo nello schema XML.

Questo ha un sacco di implicazioni:

  • senza interfacce - non è possibile passare intorno interfacce - è necessario lavorare con i tipi concreti
  • alcuna eredità "automatico" - non si può semplicemente definire una classe base e passa intorno classi basate su di essa deriva - quelli devono essere specificied troppo (che è quello che l'attributo ServiceKnownType è per)
  • non generici automatici - ancora una volta, è necessario utilizzare tipi concreti invece

Questo può sembrare un sacco di restrizioni - ma è perché WCF sta utilizzando tutte le comunicazioni basate sui messaggi - non può occuparsi di referenti, eredità, generici ecc. - è necessario scriverlo.

Quindi in realtà non ho una risposta per voi - penso solo che è necessario ripensare la vostra strategia e cambiare il modo in cui client e server si scambiano informazioni su WCF.

Marc

PS: ho fatto qualche ricerca più, e contrariamente a tutta la mia comprensione, sembra che ci sia un modo per serializzare tutto ciò che si basa su un'interfaccia e/o classe di base astratta attraverso il filo, a condizione come si può essere sicuri che sia sempre solo .NET su entrambe le estremità del cavo (ovvero è non interoperabile con ad esempio Java).

Vedi Aaron Skonnard blog post on the NetDataContractSerializer e un altro blog post e yet another che mostra come utilizzare il NetDataContractSerializer di essere in grado di passare in giro le cose come IPerson come parametri ai vostri metodi.

+0

Sono disposto a dare a WCF tutti i suggerimenti necessari ma non posso evitare di usare le interfacce oi generici ... se in qualche modo riesco a liberarmi delle interfacce posso ancora usare i generici ?? inoltre è vero il contrario se riesco a liberarmi dei generici posso ancora usare le interfacce ... il che significa che è solo la combinazione dei 2 che mi stanno causando problemi ?? Perché potrei creare una versione più specifica di IActionResult che non richiede l'uso dei generics ... –

+0

Ho aggiornato il mio post con alcune nuove informazioni che ho trovato durante la ricerca di questo argomento - spero che questo aiuti! –

+0

Grazie per l'aggiornamento ... nel mio caso NetDataContractSerializer funziona alla grande ... –

0

Stai specificando interfacce in oggetti e non tipi concreti?

Modifica:

Quello che sto dicendo è che sono tutti i tipi concreti che si stanno passando i farmaci generici (tra cui in oggetto sub tramite interfacce) essere passato nella lista di tipi conosciuti. Abbiamo avuto problemi di serializzazione in cui non erano noti tutti i tipi.

+0

che cosa si intende per quella? L'oggetto concreat implementa l'interfaccia ... – vdhant

0

Si sta restituendo un IList di T. Forse il sistema ha problemi a scoprire cos'è T.

Non sono sicuro se è possibile restituire un'interfaccia anziché un tipo.

2

Questo è uno dei problemi che risolvo con ServiceStack.NET - My Open Source .NET e MONO Web Services Framework.

Service Stack è stato fortemente influenzato da Martin Fowlers Data Transfer Object Pattern in quanto consente di utilizzare semplicemente DTO per definire i servizi Web, ad esempio SOA way :).

Evito questa limitazione inerente a WCF generando i propri WSDL che si comportano come ci si aspetterebbe. Come vantaggio di sostituire la complessa configurazione di WCF/il modello di ServiceContract - I servizi Web SOAP funzionano anche su MONO - see the live demo.

+0

Sembra interessante ma il collegamento demo live è un 404 –

1

È una vecchia domanda e anche se la risposta accettata è completamente corretta, mi sono accontentato di questo nella ricerca di un problema simile e ho pensato di condividere le mie esperienze. Spesso è un mal di testa, ma è possibile utilizzare i generici combinati con le interfacce con WCF. Ecco un esempio di lavoro di un'altra implementazione (simile) che ho fatto:

[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 chiave qui è quello di utilizzare la combinazione di KnownType e ServiceKnownType.

Quindi nel tuo caso si può fare qualcosa di simile:

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

Ciò funzionerà se si dispone di un contratto condiviso (l'accesso all'interfaccia di servizio effettivo) e consumare il contratto con ChannelFactory<ISportProgramBl>. Non so se funziona con un riferimento al servizio.

Tuttavia, sembra esserci alcuni problemi con l'attuazione come detto qui:

WCF With a interface and a generic model

E un'altra domanda simile chiesto e ha risposto qui:

Generic return types with interface type params in WCF