2009-08-31 17 views
5

Tengo una aplicación donde el cliente y el servidor comparten tipos, y la interoperabilidad no es una de nuestras preocupaciones. Estoy planeando tener un repositorio único para todos los objetos habilitados para la web, y estaba pensando en una interfaz genérica para mi servicio expuesto.wcf exponing generics

algo así como T GetObject (int id)

pero WCF no le gusta que desde su tratar de exponer su esquema (que realmente no importa)

es posible hacer tal cosa con WCF ?, puedo usar cualquier tipo de enlace no tiene que ser httpbinding o wsbinding ...

+0

Consulte también http://stackoverflow.com/questions/6223886/servicio-de-servicio-interno –

Respuesta

2

supongo que esto es posible, aunque no estoy seguro de que quieres esto. Tomaría el siguiente enfoque (no probado, no estoy seguro de si funciona).En primer lugar crear la siguiente estructura del proyecto en su solución:

  • ServiceInterfaces
  • ServiceImplementations (referencias ServiceInterfaces y ModelClasses)
  • ModelClasses
  • Host (Referencias ServiceInterfaces y ServiceImplementations)
  • Client (referencias ServiceInterfaces y ModelClasses)

En ServiceInterfaces tiene una interfaz como esta (Me salté los espacios de nombres, etc., para que el ejemplo sea más corto):

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

En ServiceImplementations tiene una clase que 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. 
    } 
} 

En Host tiene la configuración correcta para su servicio en un archivo App.config (o Web.config) y el siguiente código para alojar su servicio (dado que es una aplicación independiente) :

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

Y finalmente en Client se utiliza una clase ChannelFactory<TChannel> para definir 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); 

Una vez más, no estoy seguro de si esto funciona. El truco es compartir sus interfaces de servicio (en ServiceInterfaces) y los objetos del modelo de dominio (en ModelClasses) entre el host y el cliente. En mi ejemplo, uso una cadena para regresar del método de servicio, pero podría ser cualquier tipo de contrato de datos del proyecto ModelClasses.

+0

puedo hacer referencia a los mismos ensambles en el cliente y usar la pestaña de avance del utlity de referencia del servicio (adivino svcutil) para compartir tipos, eso funciona, pero ahora quiero hacer es abstracto, supongo que estoy buscando un tipo de remotinto solución, pero debería ser posible en wcf ... –

+0

No debe usar el cuadro de diálogo "Agregar referencia de servicio" en Visual Studio para mi enfoque. Eso no va a funcionar. Es por eso que uso la clase ChannelFactory . Genera una implementación proxy de su interfaz de servicio sobre la marcha, sin la necesidad de metadatos. –

+0

Es muy probable que esto no funcione (no puedo probarlo ahora), ya que define un servicio con operaciones que devuelven un tipo genérico "T", pero para que funcione correctamente, ese tipo "T" debe ser un DataContract, para que la pila WCF pueda serializar y deserializar objetos de ese tipo. Recuerde: todo lo que pasa entre el cliente y el servidor son ** mensajes ** - ¡no hay referencia de objeto, referencia de interfaz, ni nada basado en la referencia! ** Debes ** poder serializar tu solicitud en algún formato de mensaje, y deserializarla en el otro extremo; eso no funcionará con los genéricos :-( –

8

No, no puedes. Ya sea que desee o necesite interoperabilidad, la base más básica de WCF es el intercambio de mensajes.

El cliente envía un mensaje al servidor y recibe una respuesta. Ese mensaje es todo lo que pasa entre el cliente y el servidor, y necesita ser serializable en un formato XML o binario. Es por eso que cualquier información que se transmita debe ser atómica (como int, cadena) o un DataContract: una descripción de la pila de servicios WCF sobre cómo serializar y deserializar dichos objetos.

No se puede pasar ninguna interfaz u otro "truco": todo lo que va entre el cliente y el servidor debe poder expresarse en un esquema XML, básicamente.

Así que me temo que lo que estás tratando de lograr es bastante contrario a lo que ofrece WCF. El mundo y los paradigmas de SOA (Aplicaciones orientadas a servicios) son bastante diferentes y no siempre están 100% sincronizados con la idea y los mecanismos de OOP.

Marc

+0

por lo que si alguien quisiera hacer esto, ¿qué pila de tecnología ofrece MS? ¿Es más adecuada para la comunicación remota (si aún está activa)? ? Ms extiende soporte para compartir tipos (netcontractserializer etc) –

+0

No sé si hay alguna solución actual para objetos remotos, que es básicamente lo que estás tratando de hacer. Este enfoque tiene su cuota de problemas, que MS intenta resolver con WCF, pero para resolverlo, uno tiene que ir a un modelo totalmente basado en mensajes, que no funciona bien con interfaces y genéricos. Sí, admite el uso compartido de tipos CONCRETOS, pero definitivamente no interfaces, y realmente no genéricos. –

0

PUEDE HACER eso si utiliza ServiceKnownTypesDiscovery.

Por ejemplo:

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

donde GetKnownTypes podría ser declarado como tal:

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

En este caso todo lo declarado con [DataContract] (siempre y cuando se pueden descubrir en el servidor y el lado del cliente) se puede serializar.

¡Espero que haya sido útil!

+1

Pero ... su solución no utiliza genéricos –

0

Siguiendo el ejemplo anterior, puede declarar DataContract con un objeto como DataMember. Luego, podría agregar un método de extensión para obtener y establecer un tipo genérico en el miembro de datos del objeto. También podría hacer que esto sea interno, de esta manera estaría obligado a usar los métodos de extensión para obtener y establecer el valor.

Por supuesto, solo funciona si genera el cliente usando svcutil (o Visual Studio) y hace referencia al conjunto que contiene el contrato de datos y la clase con los métodos de extensiones.

Espero que esto ayude ...