2009-03-18 13 views

risposta

1

In realtà ho trovato una soluzione piacevole in cui combino i file xml per le sostituzioni e utilizzo le registrazioni fluenti per i valori predefiniti.

L'API fluente prende il nome completo di impl come chiave predefinita. Al volo sovrascrivo l'id di xml-config per imitare le convenzioni chiave dell'API fluente.

Quindi registro xml-config mentre ascolto Kernel.ComponentRegistered.

Successivamente, aggiungo solo servizi dal codice di configurazione in cui xml non ha ancora definito il servizio.

(si tratta di qualche tempo fa e ho appena copia-incollato il codice. Auspicabilmente, si ottiene a lavorare. Farò le modifiche, se avete dei problemi)

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

Dopo di che, prima di registrare i servizi nel codice, controllo se sono già stati registrati. Ho anche creato una classe base che rende questo più facile.Si tratta di una configurazione dell'applicazione:

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

     // .... 
    } 
} 

La base che fa il cablaggio: (La registrazione è da 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

Sì, lo fa ridefinire implementazione predefinita di un servizio. Perchè fai questo? Perché non registrare questo in primo luogo, invece di ridefinirlo?

Potrebbe fornire più contesto?

+0

Ho un file xml che sovrascrive i collegamenti fluenti. Ma l'ho risolto ora leggendo prima il file xml e non aggiungendo le configurazioni dal codice, quando il file xml le ridefinisce. –

+0

Questo è l'approccio migliore –

+0

@Lars: puoi essere più specifico di come lo fai? Ho lo stesso problema qui, ho i miei cablaggi principali e voglio passare a un'implementazione con un'altra senza dover modificare i cablaggi principali. – Marcus

9

Sono d'accordo con Krzysztof sul fatto che di solito non è una buona idea ... Tuttavia, per quanto posso dire OverWrite() non sovrascrive il componente predefinito, semplicemente sovrascrive lo stile di vita definito dall'attributo (cioè [Singleton]) .

Se si desidera sostituire un componente, è possibile utilizzare container.Kernel.RemoveComponent(string key) seguito dalla registrazione del nuovo componente.

Here's an example dove questo ha ha senso.

+1

Va notato che RemoveComponent non funziona sempre –

+0

@George: puoi approfondire ciò? dovrebbe funzionare a patto che nessun altro componente dipenda da quello che tenta di essere rimosso (il che ha senso) –

+0

Ah, capisco, ma come specificare quindi un'implementazione alternativa? Non voglio rimuovere l'intero grafico e riaggiungerlo. –

3

Questo sarebbe il tipo di problema che potrebbe essere risolto meglio impostando il contenitore con un decoratore, in cui è possibile modificare in modo esplicito l'implementazione che il decoratore indirizza le chiamate ... soprattutto se si desidera sostituire l'implementazione che sono stati iniettati componenti esistenti (ad es. singoletti) che potrebbero esistere per tutta la durata della vostra applicazione.

Ho davvero bisogno di più informazioni su ciò che stai cercando di ottenere.


Here is more information about registering Decorators with Windsor.

3

Avevo bisogno di passare a un'implementazione di un componente durante l'esecuzione di un sistema in una suite di test di integrazione e non riuscivo a utilizzare container.Kernel.RemoveComponent(). Così ho finito con uno simple facility che si occupa di questo per me.

+0

purtroppo tutti gli override devono essere specificati prima della registrazione –

11

Si potrebbe molto semplicemente fare questo nel luogo è necessario eseguire l'override l'implementazione predefinita. Questo è un esempio dei nostri test di integrazione. Entrambe le implementazioni sono ora registrate ma il tuo codice utilizzerà quello predefinito, che è quello che hai appena registrato. Funziona come una bomba e non ha alcun impatto sul resto dell'applicazione:

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

IsDefault API non è disponibile negli assembly del castello di windsor cui ho fatto riferimento. Mi riferisco alla versione 2.5.1.0 dell'assembly Castle.Core. Mi riferisco a un'assemblea più vecchia? – RBT

Problemi correlati