2012-04-10 23 views
6

Stworzyłem trzy zespoły. Witryna internetowa, usługa WCF i zestaw kontraktów zawierający interfejsy implementowane przez usługi. Chciałbym użyć Castle Windsor do stworzenia usług dla mnie na kliencie (stronie internetowej), tak, że nie muszę mieć punktu końcowego w web.config strony internetowej dla każdej usługi, której chcę użyć.Używanie Castle Windsor WcfFacility do tworzenia punktów końcowych klienta

Chciałbym spojrzeć na montaż kontraktowy i uzyskać wszystkie interfejsy usług w przestrzeni nazw. W tej chwili dla każdej usługi mam coś w rodzaju następującego po zarejestrowaniu komponentów w kontenerze.

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

oraz w moim web.config Mam kod instalacyjny.

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

I skończyć z wielu punktów końcowych usługi, które wszystkie wyglądają niemal dokładnie taki sam, a kiedy wdrożyć na komputerach klientów oni muszą ustawić adres każdego punktu końcowego choć bazowy adres URL jest taki sam dla każdego.

Chciałbym mieć bazowy URL w moim web.config, który jest przechwytywany przez kod, a następnie mieć usługi zarejestrowane w kontenerze za pomocą refleksji na temat montażu kontraktów. Potrzebuję wyspecjalizowanego zachowania punktu końcowego, które znajduje się w powyższym pliku konfiguracyjnym.

Gdzie zacząć? WcfFacility wygląda świetnie, ale doktor trochę brakuje ...

Odpowiedz

12

Zgadzam się, że brakuje dokumentów dla obiektu wcf i jest to smutne, ponieważ jest to naprawdę wspaniałe narzędzie i byłoby wielkim wstydem, gdyby ludzie nie zrobili ". t go używać, ponieważ nie mogli zacząć, więc pozwól mi zobaczyć, czy mogę ci pomóc trochę, jeśli mogę ...

Stwórzmy stosowanie trzech projektu, który ma:

  1. klasy a biblioteka współużytkowanych umów
  2. Aplikacja konsolowa, która działa jako serwer
  3. Aplikacja konsoli, który działa jako klient

Chodzi o to, że chcemy, aby móc korzystać z nazwy usług, kiedy zarejestrować usługi i dzielić bazowy URL (myślę, że to, co pan pyta a jeśli nie, miejmy nadzieję, że można się z tego ekstrapolować). Tak więc, po pierwsze, wspólne kontrakty po prostu ma to w niej (nic specjalnego, normalna WCF taryfy):

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

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

Teraz aplikacja konsoli serwera wygląda to tak, że po pierwsze wdrożenie umów o świadczenie usług (znowu nic specjalnego tam, po prostu klasy implementujące interfejsy), a następnie po prostu zarejestrować je wszystkie jako usługi (nie trzeba tutaj zapisywać żadnego pliku konfiguracyjnego) i można zmienić sposób, w jaki decydujesz o tym, czym są usługi itp., używając wszystkich opcji, które daje ci Windsor - mój schemat jest nieco ograniczony, ale daje ci pomysł):

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

A to jest serwer, teraz jak korzystać z tych usług? No, rzeczywiście, że jest dość łatwe, tutaj jest aplikacją konsoli klienta (odwołuje tylko umów klasy biblioteki):

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

To jest to - mam nadzieję, że to będzie Ci zacząć (uważam, że eksperymentowanie i korzystania z intellisense zwykle doprowadza mnie tam, gdzie muszę iść).Pokazałem ci zarówno stronę usługową, jak i klienta, ale możesz użyć jednego lub drugiego, jeśli wolisz.

Powinieneś być w stanie zobaczyć, gdzie jest skonfigurowane wiązanie i jak zbudowałem adresy URL, więc w twoim przypadku możesz łatwo po prostu zerwać podstawowy adres URL z pliku konfiguracyjnego lub cokolwiek chcesz zrobić.

Ostatnią rzeczą wspomnieć, że można dodać niestandardowe zachowania końcowego przez dodanie go jako rozszerzenie do punktu końcowego, więc na przykład klient to masz coś takiego:

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

że działa jak urok, dziękuję bardzo. –

+0

co to robi? (f => f.CloseTimeout = TimeSpan.Zero) –

+0

Ustawia domyślny czas zamykania dla wszystkich usług - jest to "Wartość TimeSpan określająca przedział czasu przewidziany dla zakończenia operacji. Wartość ta powinna być większa lub równa równa zero, domyślnie 00:01:00. " - z http://msdn.microsoft.com/en-us/library/ms731361.aspx. Również tutaj jest dobry wątek, który mówi o wszystkich możliwych limitów czasu: http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/84551e45-19a2-4d0d-bcc0-516a4041943d/ – kmp