2012-04-10 19 views
6

Ho creato tre assiemi. Un sito Web, un servizio WCF e un assembly di contratti che contiene le interfacce implementate dai servizi. Vorrei utilizzare Castle Windsor per creare i servizi per me sul client (sito Web) in modo che non debba avere un endpoint nel web.config del sito Web per ciascun servizio che desidero utilizzare.Utilizzo di Castle Windsor WcfFacilità di creazione degli endpoint client

Vorrei esaminare l'assembly del contratto e ottenere tutte le interfacce di servizio in uno spazio dei nomi. In questo momento per ogni servizio ho qualcosa di simile al seguente quando si registrano i componenti con il contenitore.

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

e nel mio web.config ho il codice di installazione.

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

io alla fine con più endpoint di servizio che tutti guardano quasi esattamente lo stesso e quando si distribuisce su clienti macchine che devono impostare l'indirizzo di ogni endpoint, anche se l'URL di base è la stessa per tutti.

Mi piacerebbe avere un URL di base nel mio web.config che viene afferrato attraverso il codice e quindi avere i servizi registrati con il contenitore utilizzando la riflessione sull'assembly contratti. Ho bisogno del comportamento dell'endpoint specializzato che si trova nel file di configurazione di cui sopra.

Dove posso iniziare? la WcfFacility sembra ottima ma il doco è un po 'carente ...

risposta

12

Sono d'accordo che mancano i documenti per la struttura del wcf e questo è triste perché è uno strumento davvero eccezionale e sarebbe un vero peccato se la gente non lo facesse t usarlo perché non potevano iniziare, quindi fammi vedere se posso dare una mano un po ', se posso ...

Creiamo un'applicazione di tre progetto che ha:

  1. una classe libreria per contratti condivisi
  2. Un'applicazione console che funge da server
  3. un'applicazione console che agisce come un client

L'idea è che noi vogliamo essere in grado di utilizzare i nomi dei servizi quando si registrano i servizi e per condividere un URL di base (penso che è quello che stavi chiedendo e se no, si spera che tu possa estrapolare da qui). Quindi, in primo luogo, ha i contratti condivisi semplicemente questo in esso (niente di speciale, tariffa normale WCF):

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

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

Adesso l'applicazione console del server assomiglia a questo, abbiamo innanzitutto attuare i contratti di servizio (ancora niente di speciale lì, proprio classi che implementano le interfacce) e quindi registrarli tutti come servizi (non notare alcun file di configurazione qui e puoi cambiare il modo in cui decidi quali servizi ecc. utilizzando tutte le opzioni che ti offre Windsor - il mio schema è un po 'limitato ma ti dà un'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) 
     { 
     } 
    } 
} 

E questo è il server, ora come consumare questi servizi? Beh, in realtà, che è abbastanza facile, qui è un'applicazione console client (si fa riferimento solo la libreria di classi contratti):

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

Questo è tutto - spero che questo permetterà di cominciare (ho scoperto che la sperimentazione e l'utilizzo del intellisense solito mi porta dove devo andare).Ti ho mostrato entrambi i lati del servizio e del cliente, ma puoi semplicemente usare l'uno o l'altro se preferisci.

Dovresti essere in grado di vedere dove è stato configurato il binding e come sono andato a costruire gli URL, quindi nel tuo caso potresti facilmente estrarre il tuo URL di base da un file di configurazione o qualsiasi cosa tu voglia fare.

Un'ultima cosa da ricordare è che è possibile aggiungere il vostro comportamento endpoint personalizzato aggiungendolo come un'estensione al punto finale, quindi nell'esempio cliente si dovrebbe avere qualcosa di simile:

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

che funziona come un fascino, grazie mille. –

+0

cosa fa questo? (f => f.CloseTimeout = TimeSpan.Zero) –

+0

Imposta il valore di chiusura predefinito per tutti i servizi, ovvero "Un valore TimeSpan che specifica l'intervallo di tempo previsto per il completamento di un'operazione di chiusura. Questo valore deve essere maggiore o uguale a uguale a Zero. L'impostazione predefinita è 00:01:00. " - da http://msdn.microsoft.com/en-us/library/ms731361.aspx. Anche qui c'è un buon thread che parla di tutti i possibili timeout: http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/84551e45-19a2-4d0d-bcc0-516a4041943d/ – kmp