2012-04-10 10 views
6

He creado tres conjuntos. Un sitio web, un servicio WCF y un conjunto de contratos que contiene las interfaces que implementan los servicios. Me gustaría utilizar Castle Windsor para crear los servicios para mí en el cliente (sitio web) para que no tenga que tener un punto final en el sitio web del sitio para cada servicio que deseo utilizar.Uso de Castle Windsor WcfFacility para crear extremos del cliente

Me gustaría ver el conjunto de contratos y obtener todas las interfaces de servicio en un espacio de nombres. Ahora mismo para cada servicio tengo algo como lo siguiente al registrar los componentes con el contenedor.

container.Register(Component.For<ChannelFactory<IMyService>>().DependsOn(new { endpointConfigurationName = "MyServiceEndpoint" }).LifeStyle.Singleton); 
container.Register(Component.For<IMyService>().UsingFactoryMethod((kernel, creationContext) => kernel.Resolve<ChannelFactory<IMyService>>().CreateChannel()).LifeStyle.PerWebRequest); 

y en mi web.config Tengo el código de configuración.

<system.serviceModel> 
     <extensions> 
     <behaviorExtensions> 
      <add name="AuthToken" type="MyNamespace.Infrastructure.AuthTokenBehavior, MyNamespace.Contracts" /> 
     </behaviorExtensions> 
     </extensions> 
     <behaviors> 
     <endpointBehaviors> 
      <behavior> 
       <AuthToken /> 
      </behavior> 
     </endpointBehaviors> 
     </behaviors> 

     <bindings> 
     <wsHttpBinding> 
      <binding maxBufferPoolSize="2147483647" maxReceivedMessageSize="2147483647" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"> 
       <readerQuotas maxStringContentLength="2147483647" maxArrayLength="2147483647"></readerQuotas> 
       <security mode="None" /> 
      </binding> 
     </wsHttpBinding> 
     </bindings> 

     <client> 
     <endpoint name="MyServiceEndpoint" address="http://someurl/MyService.svc" binding="wsHttpBinding" contract="MyNamespace.Contracts.IMyService"></endpoint> 
     </client> 
    </system.serviceModel> 

termino con varios puntos finales de servicio que todos se ven casi exactamente el mismo y cuando hacemos uso en máquinas clientes tienen que ajustar la dirección de todos los puntos finales a pesar de que la URL base es el mismo para cada uno.

Me gustaría tener una url base en mi web.config que sea atrapada a través del código y luego tenga los servicios registrados en el contenedor usando la reflexión en el ensamblaje de contratos. Necesito el comportamiento especializado del punto final que se encuentra en el archivo de configuración anterior.

¿Dónde empiezo? el WcfFacility se ve muy bien, pero el doco es un poco escaso ...

Respuesta

12

Estoy de acuerdo en que los documentos para el servicio wcf son deficientes y eso es triste porque es una gran herramienta y sería una verdadera lástima si la gente no lo hiciera t lo utilizan porque no podían empezar, así que vamos a ver si lo puedo ayudar un poco si puedo ...

Vamos a crear una aplicación de tres proyectos que tiene:

  1. una clase biblioteca para contratos compartidos
  2. Una aplicación de consola que actúa como servidor
  3. Una aplicación de consola que actúa como un cliente

La idea es que queremos ser capaces de utilizar los nombres de servicio cuando nos registramos los servicios y para compartir una URL base (creo que es lo que pedían y si no, con suerte puedes extrapolar desde aquí). Así, en primer lugar, los contratos compartidos simplemente tiene esto en él (nada especial, tarifa normal WCF):

[ServiceContract] 
public interface IMyService1 
{ 
    [OperationContract] 
    void DoSomething(); 
} 

[ServiceContract] 
public interface IMyService2 
{ 
    [OperationContract] 
    void DoSomethingToo(); 
} 

Ahora la aplicación de consola de servidor se parece a esto, en primer lugar, poner en práctica los contratos de servicio (de nuevo nada especial, simplemente clases que implementan interfaces) y luego simplemente regístrelos todos como servicios (note que no necesita ningún archivo de configuración aquí y puede cambiar la forma en que decide qué son los servicios, etc. usando todas las opciones que Windsor le ofrece; mi esquema es un poco limitado pero le da una idea):

namespace Services 
{ 
    public class MyService1 : IMyService1 
    { 
     public void DoSomething() 
     { 
     } 
    } 

    public class MyService2 : IMyService2 
    { 
     public void DoSomethingToo() 
     { 
     } 
    } 
} 

//... In some other namespace... 

class Program 
{ 
    // Console application main 
    static void Main() 
    { 
     // Construct the container, add the facility and then register all 
     // the types in the same namespace as the MyService1 implementation 
     // as WCF services using the name as the URL (so for example 
     // MyService1 would be http://localhost/MyServices/MyService1) and 
     // with the default interface as teh service contract 
     var container = new WindsorContainer();    
     container.AddFacility<WcfFacility>(
      f => f.CloseTimeout = TimeSpan.Zero); 
     container 
      .Register(
       AllTypes 
        .FromThisAssembly() 
        .InSameNamespaceAs<MyService1>() 
        .WithServiceDefaultInterfaces() 
        .Configure(c => 
           c.Named(c.Implementation.Name) 
            .AsWcfService(
             new DefaultServiceModel() 
              .AddEndpoints(WcfEndpoint 
                  .BoundTo(new WSHttpBinding()) 
                  .At(string.Format(
                   "http://localhost/MyServices/{0}", 
                   c.Implementation.Name) 
                  ))))); 

     // Now just wait for a Q before shutting down 
     while (Console.ReadKey().Key != ConsoleKey.Q) 
     { 
     } 
    } 
} 

Y ese es el servidor, ¿ahora cómo consumir estos servicios? Bueno, en realidad que es bastante fácil, aquí es una aplicación de consola de cliente (que hace referencia sólo la biblioteca de clases de contratos):

class Program 
{ 
    static void Main() 
    { 
     // Create the container, add the facilty and then use all the 
     // interfaces in the same namespace as IMyService1 in the assembly 
     // that contains the aforementioned namesapce as WCF client proxies 
     IWindsorContainer container = new WindsorContainer(); 

     container.AddFacility<WcfFacility>(
      f => f.CloseTimeout = TimeSpan.Zero); 

     container 
      .Register(
       Types 
        .FromAssemblyContaining<IMyService1>() 
        .InSameNamespaceAs<IMyService1>() 
        .Configure(
         c => c.Named(c.Implementation.Name) 
           .AsWcfClient(new DefaultClientModel 
                { 
                 Endpoint = WcfEndpoint 
                  .BoundTo(new WSHttpBinding()) 
                  .At(string.Format(
                   "http://localhost/MyServices/{0}", 
                   c.Name.Substring(1))) 
                }))); 

     // Now we just resolve them from the container and call an operation 
     // to test it - of course, now they are in the container you can get 
     // hold of them just like any other Castle registered component 
     var service1 = container.Resolve<IMyService1>(); 
     service1.DoSomething(); 

     var service2 = container.Resolve<IMyService2>(); 
     service2.DoSomethingToo(); 
    } 
} 

Eso es todo - espero que esto le ayudará a utilizar (me parece que la experimentación y el uso de la intelisense general me lleva a donde tengo que ir).Te mostré el servicio y el lado del cliente, pero puedes usar uno u otro si lo prefieres.

Debería poder ver dónde está configurado el enlace y cómo he ido construyendo las URL, así que en su caso podría simplemente arrancar su URL base de un archivo de configuración o lo que sea que desee hacer.

Una última cosa a mencionar es que se puede añadir su comportamiento variable personalizada añadiéndolo como una extensión hasta el punto final, por lo que en el ejemplo de cliente que tener algo como esto:

Endpoint = WcfEndpoint 
    .BoundTo(new WSHttpBinding()) 
    .At(string.Format("http://localhost/MyServices/{0}", c.Name.Substring(1))) 
    .AddExtensions(new AuthTokenBehavior()) 
+0

que funciona como una encanto, muchas gracias. –

+0

¿Qué hace esto? (f => f.CloseTimeout = TimeSpan.Zero) –

+0

Establece el límite de tiempo predeterminado para todos los servicios; esto es "Un valor TimeSpan que especifica el intervalo de tiempo proporcionado para que se complete una operación de cierre. Este valor debe ser mayor que o igual a cero. El valor predeterminado es 00:01:00. " - desde http://msdn.microsoft.com/en-us/library/ms731361.aspx. También aquí hay un buen hilo que habla de todos los tiempos de espera posibles: http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/84551e45-19a2-4d0d-bcc0-516a4041943d/ – kmp

Cuestiones relacionadas