2009-03-18 12 views

Antwort

1

Ich fand tatsächlich eine nette Lösung, wo ich xml-Dateien für Überschreibungen kombinieren und flüssige Registrierungen für Standardwerte verwenden.

Die fluent-API verwendet den vollständigen Namen eines impl als Standardschlüssel. Im laufenden Betrieb überschreibe ich die IDs von xml-config, um die Schlüsselkonventionen der fluent-API zu imitieren.

Dann registriere ich die Xml-Config, während ich Kernel.ComponentRegistered höre.

Danach habe ich nur Dienste von der Code-Config, wo die XML noch nicht den Dienst definiert.

(es ist eine Weile her und ich nur den Code kopieren kleistert. Hoffentlich bekommen Sie daran zu arbeiten. Ich werde Änderungen tun, wenn Sie irgendwelche Probleme finden)

IList<Type> unnamedServices = new List<Type>(); 
IDictionary<string, Type> namedServices = new Dictionary<string, Type>(); 

ComponentDataDelegate registered = captureRegistrations(unnamedServices, namedServices); 

container.Kernel.ComponentRegistered += registered; 

// The method that captures the services 
private static ComponentDataDelegate captureRegistrations(
    IList<Type> unnamedServices, IDictionary<string, Type> namedServices) 
{ 
     return (key, handler) => 
       { 
        if (handler.ComponentModel.Name == handler.ComponentModel.Implementation.FullName) 
        { 
         unnamedServices.Add(handler.Service); 
        } 
        else 
        { 
         namedServices.Add(key, handler.Service); 
        } 
       }; 
} 

Danach bevor ich registrieren Im Code überprüfe ich, ob sie bereits registriert sind. Ich habe auch eine Basisklasse erstellt, die das vereinfacht.Dies ist eine Anwendungskonfiguration:

public class ApplicationConfiguration : WindsorConfigurationSkeleton 
{ 
    internal static WindsorServiceLocator create() 
    { 
     var container = createWith(null, "components-config.xml", coreServices, caches, roles); 
     return new WindsorServiceLocator(container); 
    } 

    internal static IEnumerable<IRegistration> coreServices() 
    { 
     yield return Component.For<ISystemClock>() 
      .ImplementedBy<PreciseSystemClock>() 
      .Parameters(Parameter.ForKey("synchronizePeriodSeconds").Eq("10")) 
      .LifeStyle.Singleton; 

     yield return Component.For<IMailService>() 
      .ImplementedBy<MailQueueService>() 
      .LifeStyle.Singleton; 
    } 

    internal static IEnumerable<IRegistration> caches() 
    { 
     yield return Component.For<IDataCache<ServiceAttributes>>() 
      .ImplementedBy<NoDataCache<ServiceAttributes>>() 
      .LifeStyle.Singleton; 

     // .... 
    } 
} 

Die Klasse Basis, die die Verkabelung funktioniert: (Protokollierung von commons.logging)

public class WindsorConfigurationSkeleton 
{ 
    private static readonly ILog _log = LogManager.GetLogger(
     typeof(WindsorConfigurationSkeleton)); 

    internal static IWindsorContainer createWith(
     IRegistration[] customs, string configFile, params Func<IEnumerable<IRegistration>>[] methods) 
    { 
     IWindsorContainer container = new WindsorContainer(); 
     BugFix.Kernel = container.Kernel; 

     container.AddFacility("factory.support", new FactorySupportFacility()); 

     IList<Type> unnamedServices = new List<Type>(); 
     IDictionary<string, Type> namedServices = new Dictionary<string, Type>(); 

     ComponentDataDelegate registered = captureRegistrations(unnamedServices, namedServices); 

     container.Kernel.ComponentRegistered += registered; 

     if (customs != null) 
     { 
      container.Register(customs); 
     } 

     if (configFile != null) 
     { 
      tryAddXmlConfig(container, configFile); 
     } 

     container.Kernel.ComponentRegistered -= registered; 

     if (methods != null && methods.Length > 0) 
     { 
      container.Register(union(unnamedServices, namedServices, methods)); 
     } 

     return container; 
    } 

    private static ComponentDataDelegate captureRegistrations(
     IList<Type> unnamedServices, IDictionary<string, Type> namedServices) 
    { 
     return (key, handler) => 
       { 
        if (handler.ComponentModel.Name == handler.ComponentModel.Implementation.FullName) 
        { 
         var text = unnamedServices.Contains(handler.Service) ? "another" : "default"; 
         _log.Info(
          m => m(
            "Registered {2} service for {0} with {1}.", 
            handler.Service.GetDisplayName(), 
            handler.ComponentModel.Implementation.GetDisplayName(), 
            text 
            )); 

         unnamedServices.Add(handler.Service); 
        } 
        else 
        { 
         var text = namedServices.ContainsKey(key) ? "another" : "default"; 
         _log.Info(
          m => m(
            "Registered {3} service {0} with name '{1}' and {2}.", 
            handler.ComponentModel.Service, 
            handler.ComponentModel.Name, 
            handler.ComponentModel.Implementation.GetDisplayName(), 
            text 
            )); 
         namedServices.Add(key, handler.Service); 
        } 
       }; 
    } 

    protected static void tryAddXmlConfig(IWindsorContainer container, string filename) 
    { 
     var fi = Resources.GetFileFromResourceHierarchy(typeof(ApplicationContext).Namespace, filename); 
     if (fi == null) { 
      return; 
     } 
     var configFile = fi.FullName; 
     var xd = immitateFluentApiDefaultIdBehaviour(configFile); 
     container.Install(Configuration.FromXml(new StaticContentResource(xd.OuterXml))); 

    } 

    private static XmlDocument immitateFluentApiDefaultIdBehaviour(string configFile) 
    { 
     var xd = new XmlDocument(); 
     xd.Load(configFile); 

     foreach (
      XmlElement component in 
       xd.SelectNodes("/configuration/components/component[@type and (not(@id) or @id = '')]")) 
     { 
      var type = Type.GetType(component.GetAttribute("type"), true); 
      component.SetAttribute("id", type.FullName); 
     } 

     return xd; 
    } 

    private static IRegistration[] union(
     IList<Type> unnamed, IDictionary<string, Type> named, params Func<IEnumerable<IRegistration>>[] methods) 
    { 
     var all = new List<IRegistration>(); 
     foreach (var method in methods) 
     { 
      foreach (var registration in method()) 
      { 
       var registrationType = registration.GetType(); 
       if (registrationType.IsGenericTypeOf(typeof(ComponentRegistration<>))) 
       { 
        var componentType = registrationType.GetGenericArgumentsFor(typeof(ComponentRegistration<>))[0]; 

        var name = (string)registrationType.GetProperty("Name").GetValue(registration, null); 

        if (name != null) 
        { 
         if (named.ContainsKey(name)) 
         { 
          _log.Debug(
           m => m("Skipped registering default named component {0}.", name)); 
          continue; 
         } 
        } 
        else if (unnamed.Contains(componentType)) 
        { 
         _log.Debug(
          m => m("Skipped registering default component for type {0}.", componentType)); 
         continue; 
        } 

        all.Add(registration); 
       } 
       else 
       { 
        all.Add(registration); 
       } 
      } 
     } 

     return all.ToArray(); 
    } 
} 
0

Ja, es Default-Implementierung eines Dienstes neu zu definieren. Warum tust du das? Warum registrieren Sie das nicht an erster Stelle, anstatt es neu zu definieren?

Könnten Sie mehr Kontext bereitstellen?

+0

Ich habe eine XML-Datei, die die fließenden Verdrahtungen überschreibt. Aber ich habe es jetzt gelöst, indem ich zuerst die XML-Datei gelesen habe und nicht die Konfigurationen aus dem Code hinzugefügt habe, wenn die XML-Datei sie neu definiert. –

+0

Das ist besser Ansatz –

+0

@Lars: Können Sie genauer sein, wie Sie das tun? Ich habe das gleiche Problem hier, ich habe meine Kern-Verdrahtungen und möchte eine Implementierung mit einer anderen wechseln, ohne die Kern-Verdrahtungen modifizieren zu müssen. – Marcus

9

Ich stimme mit Krzysztof überein, dass dies normalerweise keine gute Idee ist ... Soweit ich jedoch sagen kann, überschreibt OverWrite() die Standardkomponente nicht, sondern überschreibt nur den durch Attribut definierten Lifestyle (zB [Singleton]) . Wenn Sie eine Komponente ersetzen wollen, können Sie container.Kernel.RemoveComponent(string key) verwenden, gefolgt von der Registrierung der neuen Komponente.

Here's an example wo dies macht Sinn machen.

+1

Es sollte angemerkt werden, dass RemoveComponent nicht immer funktioniert –

+0

@George: können Sie darauf näher eingehen? es sollte funktionieren, solange keine anderen Komponenten davon abhängen, welche entfernt werden sollen (was Sinn macht) –

+0

Ah ich sehe, aber wie dann eine alternative Implementierung zu spezifizieren? Ich möchte nicht das gesamte Diagramm entfernen und neu hinzufügen. –

3

Dies wäre die Art von Problem, die besser gelöst werden könnte, indem Sie den Container mit einem Dekorator einrichten, wo Sie explizit die Implementierung des Dekorators Rufe an ... ändern können, vor allem, wenn Sie die Implementierung ersetzen möchten vorhandene Komponenten (zB Singletons) wurden injiziert, die für die Lebensdauer Ihrer Anwendung existieren könnten.

Wirklich brauchen mehr Hintergrund auf, was Sie erreichen möchten.


Here is more information about registering Decorators with Windsor.

3

Ich musste eine Komponentenimplementierung wechseln, wenn ein System in einer Integrationstest-Suite ausgeführt wurde, und konnte container.Kernel.RemoveComponent() nicht verwenden. So endete ich mit einer simple facility, die das für mich erledigt.

+0

Leider müssen alle Überschreibungen angegeben werden, bevor die Registrierung abgeschlossen ist. –

11

Sie könnten sehr einfach diese Sie außer Kraft setzen müssen an der Stelle tun, um die Standardimplementierung. Dies ist ein Beispiel aus unseren Integrationstests. Beide Implementierungen sind jetzt registriert, aber Ihr Code wird den Standard verwenden, den Sie gerade registriert haben. Funktioniert wie eine Bombe und hat keine Auswirkungen auf den Rest der Anwendung:

 var sendMailStub = MockRepository.GenerateStub<ISendMail>(); 
     _container.Register(
      Component 
       .For<ISendMail>() 
       .Instance(sendMailStub) 
       .IsDefault() 
      ); 
+0

IsDefault API ist in den von mir genannten Windsor Castle Assemblies nicht verfügbar. Ich verweise Castle.Core Assembly Version 2.5.1.0. Beziehe ich mich auf eine ältere Versammlung? – RBT