2010-07-26 12 views
6

Sto tentando di implementare un contratto di servizio che contiene un metodo che utilizza un'interfaccia generica e che a tale interfaccia generica viene assegnato un parametro di interfaccia. Ho decorato l'interfaccia di servizio con ServiceKnownType, ho decorato l'implementazione del servizio con KnownType regolare, e ho decorato l'attuazione DataContract con KnownType regolare:WCF: è possibile la serializzazione di interfacce generiche?

[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(ICallbacks))] 
[ServiceKnownType(typeof(Batch<object>))] 
[ServiceKnownType(typeof(Command))] 
public interface IActions 
{ 
    [OperationContract] 
    IResponse TakeAction(IBatch<ICommand> commands); 
} 

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Reentrant)] 
[KnownType(typeof(Batch<object>))] 
[KnownType(typeof(Command))] 
internal class Actions : IActions 
{ 
} 

[DataContract] 
[KnownType(typeof(Command))] 
public class Batch<T> : IBatch<T> 
{ 
} 

Per la cronaca, ho Batch lì perché sembra che puoi esprimere un tipo noto solo per un tipo generico una volta: sembra emettere BatchOfanyType, ma non sono sicuro di come gestirlo.

L'eccezione che sto ottenendo è "Aggiungi tutti i tipi non noti staticamente alla lista dei tipi conosciuti -., Ad esempio, utilizzando l'attributo KnownTypeAttribute o aggiungendoli alla lista dei tipi noti passati a DataContractSerializer"

C'è qualcosa di ovvio che sto sbagliando? Le interfacce generiche di interfacce non sono supportate? Per la cronaca sono su C# 2.0 e .NET 3.0 per questo progetto.

risposta

12

È possibile utilizzare le interfacce nelle definizioni del contratto di servizio se lo si desidera, a condizione che si includano i tipi noti come si sta facendo (con una leggera regolazione, vedere di seguito).

Apparentemente, l'utilizzo di un'interfaccia come parametro di tipo generico lo sta portando troppo lontano per C# 3.0. Ho cambiato l'attributo type noto per

[ServiceKnownType(typeof(Batch<Command>))] 
public interface IActions 
{ 
} 

che lo fa funzionare, a un certo punto. Serializzazione e deserializzazione per sé funziona, ma poi sei di fronte a questa eccezione:

Impossibile eseguire il cast oggetto di tipo 'Batch`1 [Command]' digitare 'IBatch`1 [ICommand]'.

Perché il cast funzioni, è necessario il supporto del linguaggio per la covarianza di tipo generico, qualcosa introdotto in C# 4.0. Per farlo funzionare in C# 4.0, però, avresti bisogno di aggiungere un modificatore di varianza:

public interface IBatch<out T> 
{ 
} 

allora funziona perfettamente ... purtroppo non si sta usando C# 4.0.

Un'ultima cosa sull'utilizzo delle interfacce nel contratto di servizio: se stai generando un riferimento al servizio da loro, digiterà tutti i parametri dell'interfaccia come object, perché il tipo di interfaccia originale non fa parte dei metadati. È possibile condividere i contratti tramite un riferimento all'assembly o rifattorizzare manualmente il proxy generato per correggerlo, ma tutto sommato, l'utilizzo delle interfacce con WCF è probabilmente più un problema che non ne vale la pena.

+0

Sì, ho modificato la piattaforma che sto usando quando ho pensato alla covarianza in C# 4.0. Oh, per l'aggiornamento. – bwerks

2

WCF è un sistema basato su messaggi SOA - può inviare qualsiasi cosa attraverso il filo nel formato XML serializzato che può essere espresso nello schema XML.

Sfortunatamente, lo schema XML non conosce nulla né interfacce né generici, quindi no - non è possibile generarli in serie generiche - è necessario utilizzare tipi concreti.

+0

WCF fa capire generici abbastanza per serializzare loro (l'ho fatto). Il tuo punto sulle interfacce è corretto. – RQDQ

+0

@RQDQ hai un posto dove mostrarlo? Un blog, un articolo CodeProject o qualcosa del genere? Mi piace vederlo! –

+1

@ RQDQ: In realtà, non è del tutto corretto, imo. Mentre l'interfaccia stessa non può essere serializzata, il tipo di calcestruzzo usato * può * essere. La parte "difficile" è la deserializzazione, perché ha bisogno di sapere quale tipo concreto istanziare. Tuttavia, sono sufficienti informazioni sufficienti nei dati serializzati per fare esattamente questo. – Thorarin

1

Non è possibile serializzare un'interfaccia. Un'interfaccia definisce solo il contratto, non l'oggetto. Immagino che l'unica eccezione a questa sia l'interfaccia ISerializable.

+1

Quando "serializzi' ISerializable' ", non lo sei, davvero. 'ISerializable' è un'interfaccia usata per serializzare qualcos'altro. –

+0

@ John. Hai ragione. Non stai ancora serializzando realmente l'interfaccia, solo concordando un contratto usato per serializzare gli oggetti che implementano ISerializable. Ho appena aggiunto questo per completezza. – Mike

1

I generici possono essere serializzati, ma con alcune limitazioni.Ad esempio, dato il contratto di dati:

[DataContract] 
public class Foo<T> 
{ 
    [DataMember] 
    public T Value { get; set; } 
} 

E il contratto di servizio:

[ServiceContract] 
public interface IService1 
{ 
    [OperationContract] 
    Foo<String> GetData(); 
} 

E l'implementazione del servizio:

public class Service1 : IService1 
{ 
    public Foo<string> GetData() 
    { 
     return new Foo<string>() { Value = "My test string" }; 
    } 
} 

Dopo aver impostato un servizio di riferimento per il servizio di cui sopra, questo il codice può essere eseguito:

ServiceReference1.Service1Client client = new ServiceReference1.Service1Client(); 

ServiceReference1.FooOfstring temp = client.GetData(); 

MessageBox.Show(temp.Value); 

E viene visualizzata la finestra di messaggio con "My test string".

Si noti che il servizio stesso non è generico, ma il contratto dati utilizzato è. Inoltre, il contratto di dati generati sul lato client non è generico, ma piuttosto una classe "appiattita" che ha un valore di proprietà di tipo stringa:

[System.Runtime.Serialization.DataMemberAttribute()] 
public string Value 
{ 
    get {...} 
    set {...} 
} 
Problemi correlati