2009-08-31 3 views
5

Ho un'applicazione in cui i tipi di condivisione client e server e l'interoperabilità non è una delle nostre preoccupazioni. Sto pianificando di avere un unico repository per tutti gli oggetti abilitati al web e stavo pensando a un'interfaccia generica per il mio servizio esposto.wcf esponendo generici

qualcosa come T GetObject (int id)

ma WCF pretende molto simile fin dalla sua cercando di esporre il relativo schema (che io veramente non interessa)

è possibile fare una cosa simile con WCF ?, posso usare qualsiasi tipo di legame non deve essere httpbinding o wsbinding ...

+0

Vedere anche http://stackoverflow.com/questions/6223886/generic-service-interface –

risposta

2

Suppongo che questo sia possibile, anche se non sono sicuro che ci si vuole questo. Prenderò il seguente approccio (non verificato, non sono sicuro che funzioni).In primo luogo creare la seguente struttura del progetto nella soluzione:

  • ServiceInterfaces
  • ServiceImplementations (riferimenti ServiceInterfaces e ModelClasses)
  • ModelClasses
  • Host (riferimenti ServiceInterfaces e ServiceImplementations)
  • Client (riferimenti ServiceInterfaces e ModelClasses)

In ServiceInterfaces si dispone di un'interfaccia come questo (ho saltato gli spazi dei nomi, ecc per fare l'esempio più breve):

[ServiceContract] 
public interface IMyService<T> 
{ 
    T GetObject(int id); 
} 

In ServiceImplementations si dispone di una classe che implementa IMyService<T>:

public class MyService<T> : IMyService<T> 
{ 
    T GetObject(int id) 
    { 
     // Create something of type T and return it. Rather difficult 
     // since you only know the type at runtime. 
    } 
} 

In Host hai la configurazione corretta per il tuo servizio in un file App.config (o Web.config) e il seguente codice per ospitare il tuo servizio (dato che si tratta di un'app standalone) :

ServiceHost host = new ServiceHost(typeof(MessageManager.MessageManagerService)) 
host.Open(); 

E infine in Client si utilizza una classe ChannelFactory<TChannel> per definire un proxy:

Binding binding = new BasicHttpBinding(); // For the example, could be another binding. 
EndpointAddress address = new EndpointAddress("http://localhost:8000/......"); 
IMyService<string> myService = 
    ChannelFactory<IMyService<string>>.CreateChannel(binding, address); 
string myObject = myService.GetObject(42); 

Anche in questo caso, non sono sicuro se questo funziona. Il trucco consiste nel condividere le interfacce di servizio (in ServiceInterfaces) e gli oggetti del modello di dominio (in ModelClasses) tra l'host e il client. Nel mio esempio, utilizzo una stringa per restituire dal metodo di servizio ma potrebbe essere qualsiasi tipo di contratto dati dal progetto ModelClasses.

+0

posso fare riferimento agli stessi assembly sul client e utilizzare la scheda di avanzamento del servizio di riferimento utlity (sto indovinando svcutil) per condividere i tipi, che funziona, ma ora voglio fare è astratto, indovina sto cercando un tipo di remotint soluzione, ma dovrebbe essere possibile su wcf ... –

+0

Non si dovrebbe usare la finestra di dialogo "Aggiungi riferimento servizio" in Visual Studio per il mio approccio. Non funzionerà. Questo è il motivo per cui utilizzo la classe ChannelFactory . Genera immediatamente un'implementazione proxy dell'interfaccia di servizio, senza necessità di metadati. –

+0

Questo probabilmente non funzionerà (non posso testarlo adesso), dal momento che definisci un servizio con operazioni che restituiscono un tipo generico "T" - ma per funzionare correttamente, quel tipo "T" deve essere un DataContract, in modo che lo stack WCF possa serializzare e deserializzare oggetti di quel tipo. Ricorda: tutto ciò che passi tra client e server sono ** messaggi ** - non c'è riferimento a oggetti, riferimenti a interfacce o qualsiasi cosa basata su riferimenti! Devi ** essere in grado di serializzare la tua richiesta in qualche formato di messaggio, e deserializzarla dall'altra parte - che non funzionerà con i generici :-( –

8

No, non è possibile. Indipendentemente dal fatto che tu voglia o necessiti dell'interoperabilità, il fondamento più basilare di WCF è lo scambio di messaggi.

Il client invia al server un messaggio e ottiene una risposta. Quel messaggio è tutto ciò che passa tra client e server e deve essere serializzabile in un formato XML o binario. Ecco perché tutti i dati trasmessi devono essere atomici (come int, string) o DataContract - una descrizione per lo stack di servizi WCF su come serializzare e deserializzare tali oggetti.

Non è possibile passare alcuna interfaccia o altri "trucchetti": in pratica, tutto ciò che va tra client e server deve essere espressivo nello schema XML.

Quindi temo che quello che stai cercando di ottenere sia del tutto contrario a quello che offre WCF. Il mondo e i paradigmi di SOA (App orientati ai servizi) sono molto diversi e non sempre al 100% in sincrono con l'idea e i meccanismi di OOP.

Marc

+0

quindi, se qualcuno volesse fare ciò, quale stack di tecnologia offre MS, è più adatto per i servizi remoti (se questo è ancora attivo) ? Ms estende il supporto per i tipi di condivisione (netcontractserializer ecc.) –

+0

Non so se esiste una soluzione corrente per la gestione remota di oggetti, che è in pratica ciò che stai cercando di fare. Questo approccio ha la sua parte di problemi, che MS cerca di risolvere con WCF - ma per risolverlo bisogna andare a un modello completamente basato sui messaggi, che non funziona bene con interfacce e generici. Sì, supporta la condivisione dei tipi CONCRETE - ma sicuramente non le interfacce e non i generici. –

0

È possibile farlo se si utilizza ServiceKnownTypesDiscovery.

Ad esempio:

[ServiceKnownType("GetKnownTypes", typeof(ServiceKnownTypesDiscovery))] 
public interface ISomeService 
{ 
    [OperationContract] 
    object Request(IRequestBase parameters); 
} 

dove GetKnownTypes potrebbe essere dichiarato in questo modo:

public static class ServiceKnownTypesDiscovery 
{ 
    public static IEnumerable<Type> GetKnownTypes(ICustomAttributeProvider provider) 
    { 
     var types = new List<Type>(); 

     foreach (var asmFile in Directory.GetFiles(AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory, "*.dll")) 
     { 
      Assembly asm = Assembly.LoadFrom(asmFile); 
      types.AddRange(asm.GetTypes().Where(p=> Attribute.IsDefined(p,typeof(DataContractAttribute)))); 
     } 

     return types; 
    } 
} 

In questo caso tutto dichiarato con [DataContract] (fintanto che sono individuabili sul server e la lato client) può essere serializzato.

Spero che questo ha aiutato!

+1

Ma ... la tua soluzione non usa i generici –

0

Seguendo l'esempio precedente, si potrebbe dichiarare una DataContract con un oggetto come DataMember. Quindi è possibile aggiungere un metodo di estensione per ottenere e impostare un tipo generico sul membro dati dell'oggetto. Potresti anche renderlo interno, in questo modo sarai obbligato a usare i metodi di estensione per ottenere e impostare il valore.

Ovviamente, funziona solo se si genera il client utilizzando svcutil (o Visual Studio) e si fa riferimento all'assembly contenente il contratto dati e la classe con i metodi estensioni.

Spero che questo aiuti ...