2012-04-10 12 views
6

Ich habe drei Assemblys erstellt. Eine Website, ein WCF-Dienst und eine Vertragsassembly, die die von den Diensten implementierten Schnittstellen enthält. Ich möchte Castle Windsor verwenden, um die Dienste für mich auf dem Client (Website) zu erstellen, so dass ich keinen Endpunkt in der web.config der Website für jeden Dienst, den ich verwenden möchte, haben muss.Verwenden von Castle Windsor WcfFacility zum Erstellen von Clientendpunkten

Ich möchte die Vertragsassembly betrachten und alle Service-Schnittstellen in einem Namespace erhalten. Im Moment habe ich für jeden Dienst Folgendes, wenn ich die Komponenten mit dem Container registriere.

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

und in meiner web.config habe ich den Setup-Code.

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

ich am Ende mit mehreren Service-Endpunkten auf, die alle fast genau gleich aussehen, und wenn wir auf Kunden Maschinen implementieren sie die Adresse von jedem Endpunkt zu setzen, obwohl die Basis-URL für jeden gleich ist.

Ich möchte eine Basis-URL in meiner web.config haben, die durch Code ergriffen wird und dann die Dienste mit dem Container mit Reflektion über die Verträge Assembly registriert haben. Ich benötige das spezielle Endpunktverhalten, das in der obigen Konfigurationsdatei enthalten ist.

Wo also fange ich an? die WcfFacility sieht gut aus, aber die doco ist ein bisschen fehlt ...

Antwort

12

Ich stimme zu, die Dokumente für die WCF-Anlage fehlen und das ist traurig, weil es ein wirklich tolles Werkzeug ist und es wäre eine Schande, wenn die Leute nicht t es verwenden, weil sie nicht beginnen konnte, so lassen Sie mich sehen, wenn ich Sie heraus ein wenig helfen kann, wenn ich kann ...

lassen Sie uns ein drei Projekt Anwendung erstellen, die hat:

  1. A-Klasse Bibliothek für gemeinsame Verträge
  2. Eine Konsolenanwendung, die als Server fungiert
  3. Eine Konsolenanwendung, die als Client ist

Die Idee wirkt, dass wir die Service-Namen zu verwenden, wenn wir die Dienste registrieren und zu teilen eine Basis-URL in der Lage sein wollen (ich glaube, das ist, was Sie fragen, und wenn nicht, hoffentlich können Sie von hier aus extrapolieren). Also erstens hat die gemeinsamen Verträge einfach diese darin (nichts Besonderes, normale WCF-Tarif):

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

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

Nun ist die Server-Konsole wie folgt aussieht, wir setzen zunächst die Dienstleistungsaufträge (wieder dort nichts Besonderes, nur Klassen implementieren Schnittstellen) und dann registrieren sie alle als Dienste (beachten Sie keine Notwendigkeit für eine Konfigurationsdatei hier und Sie können ändern, wie Sie entscheiden, was Dienste usw. mit allen Optionen sind, die Windsor gibt Ihnen - mein Schema ist ein bisschen begrenzt, aber es gibt Ihnen eine Idee):

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) 
     { 
     } 
    } 
} 

Und das ist der Server, jetzt, wie diese Dienste zu verbrauchen? Na ja, eigentlich das ist ganz einfach, hier ist eine Client-Konsole-Anwendung (es verweist nur die Verträge Klassenbibliothek):

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

Das ist es - hoffentlich werden Sie beginnen (ich finde, dass das Experimentieren und mit der Intellisense in der Regel bringt mich dahin, wo ich hingehen muss).Ich habe Ihnen sowohl die Service- als auch die Client-Seite gezeigt, aber Sie können nur die eine oder andere verwenden, wenn Sie bevorzugen.

Sie sollten sehen können, wo die Bindung konfiguriert ist und wie ich die URLs aufgebaut habe, in Ihrem Fall könnten Sie einfach Ihre Basis-URL aus einer Konfigurationsdatei oder was auch immer Sie tun möchten.

Eine letzte Sache ist zu erwähnen, dass Sie es als Erweiterung zu dem Endpunkt, indem Sie Ihr benutzerdefinierten Endpunkt Verhalten hinzufügen können, so in dem Client-Beispiel, das Sie so etwas wie dies haben würde:

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

, die wie ein Werk Charme, vielen Dank. –

+0

was macht das? (f => f.CloseTimeout = TimeSpan.Zero) –

+0

Das legt das Standard-closetimeout für alle Dienste fest - das ist "Ein TimeSpan-Wert, der das Zeitintervall angibt, das für einen Abschlussvorgang bereitgestellt wird. Dieser Wert sollte größer als oder sein gleich Null. Der Standardwert ist 00:01:00. " - von http://msdn.microsoft.com/en-us/library/ms731361.aspx. Auch hier ist ein guter Thread, der über alle möglichen Timeouts spricht: http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/84551e45-19a2-4d0d-bcc0-516a4041943d/ – kmp

Verwandte Themen