2

Compte tenu de la mise en œuvre ci-dessous dans le château de windsor 3.4.0:Château Windsor UsingFactoryMethod avec LifestyleTransient

public class ExampleInstaller : IWindsorInstaller 
{ 
    public void Install(IWindsorContainer container, IConfigurationStore store) 
    { 
     container.Register(Component.For<FailoverDatabaseConnectionExecutor>() 
     .ImplementedBy<FailoverDatabaseConnectionExecutor>() 
     .LifestyleTransient()); 

     container.Register(Component.For<DatabaseConnectionExecutor>() 
     .ImplementedBy<DatabaseConnectionExecutor>() 
     .LifestyleTransient()); 

     container.Register(Component.For<IDatabaseConnectionExecutor>() 
     UsingFactoryMethod(CreateDatabaseConnectionExecutor) 
     .LifestyleTransient() 
     .IsDefault()); 
    } 

    private static IDatabaseConnectionExecutor CreateDatabaseConnectionExecutor(IKernel kernel) 
    { 
     var configurationRepository = kernel.Resolve<IConfigurationRepository>(); 

     return configurationRepository.GetSetting(ConfigurationSettings.DatabaseFailoverEnabled,() => false) 
      ? (IDatabaseConnectionExecutor)kernel.Resolve<FailoverDatabaseConnectionExecutor>() 
      : kernel.Resolve<DatabaseConnectionExecutor>(); 
    } 
} 

Le cadre est de retour l'exception suivante:

instance FailoverDatabaseConnectionExecutor du composant lié tardif IDatabaseConnectionExecutor est déjà être suivi. La méthode usine fournissant des instances du composant réutilise des instances, mais le style de vie du composant est Transitoire, ce qui nécessite une nouvelle instance chaque fois. Dans la plupart des cas, il est conseillé pour la méthode d'usine de ne pas gérer la réutilisation des instances, mais de choisir un style de vie qui le fait de manière appropriée. Sinon, si vous ne souhaitez pas Windsor pour suivre les objets provenant de l'usine changer votre regustration à « .UsingFactoryMethod (yourFactory, managedExternally: vrai) »

Il en résulte la chaîne de dépendance ne pas résoudre et une valeur nulle sur l'injection de propriété sur notre contrôleur.

Ce que nous essayons d'obtenir est un commutateur sur la résolution basée sur la valeur de configuration ConfigurationSettings.DatabaseFailoverEnabled. Nous voulons que cela se produise de manière transitoire à la fois sur l'usine et sur les types résolus sous-jacents.

De l'erreur, il semblerait cela est impossible, notre question est de savoir comment réaliser une mise en œuvre de l'usine de style tout en maintenant un cycle de vie transitoire sur les deux FailoverDatabaseConnectionExecutor et DatabaseConnectionExecutor

EDIT:

Après passer un peu de temps à enquêter plus loin, cela semble être un problème lié à l'un des objets de ma chaîne de dépendance. Lorsque l'un des objets implémente IDisposable, cette erreur se produit lorsqu'il est utilisé conjointement avec UsingFactoryMethod.

Tenir compte de la ci-dessous (simplifié) exemple:

public class ServiceInstaller : IWindsorInstaller 
{ 
    public void Install(IWindsorContainer container, IConfigurationStore store) 
    { 
     container.Register(
      Component.For<ISomeConnectionService>() 
       .ImplementedBy<SomeConnectionService>() 
       .LifestyleTransient() 
       .IsDefault()); 

     container.Register(
      Component.For<FailoverDatabaseConnectionExecutor>() 
      .ImplementedBy<FailoverDatabaseConnectionExecutor>() 
      .LifestyleTransient()); 

     container.Register(Component.For<IDatabaseConnectionExecutor>() 
      .UsingFactoryMethod(CreateDatabaseConnectionExecutor) 
      .LifestyleTransient() 
      .IsDefault()); 
    } 

    private static IDatabaseConnectionExecutor CreateDatabaseConnectionExecutor(IKernel kernel) 
    { 
     return kernel.Resolve<FailoverDatabaseConnectionExecutor>(); 
    } 
} 

public interface IDatabaseConnectionExecutor 
{ 
} 

public class SomeConnectionService : ISomeConnectionService, IDisposable 
{ 
    public SomeConnectionService() 
    { 

    } 

    public void Dispose() 
    { 
    } 
} 

public interface ISomeConnectionService 
{ 
} 

public class FailoverDatabaseConnectionExecutor : IDatabaseConnectionExecutor 
{ 
    private readonly ISomeConnectionService _someConnectionService; 

    public FailoverDatabaseConnectionExecutor(ISomeConnectionService someConnectionService) 
    { 
     _someConnectionService = someConnectionService; 
    } 
} 

Retrait IDisposable de SomeConnectionService injectera correctement la chaîne de dépendance.

Est-ce que quelqu'un sait pourquoi c'est le cas dans Castle Windsor?

+0

Veuillez essayer Release() 'the configurationRepository avant de retourner de votre méthode d'usine. Lorsque vous utilisez Castle, vous devriez avoir l'habitude d'appeler explicitement Release() pour tout ce que vous avez résolu(). –

Répondre

0

Après avoir travaillé une partie du château du code windsor il semble évident qu'il ya un problème quelque part en ce qui concerne le suivi des objets qui sont marqués pour la mise hors service.More details can be found here

Nous avons surmonté cela en passant à utiliser le IHandlerSelector. Cela nous a permis de sélectionner dynamiquement une dépendance au moment de l'exécution en fonction des paramètres de configuration.

public class RepositoriesInstaller : IWindsorInstaller 
{ 
    public void Install(IWindsorContainer container, IConfigurationStore store) 
    { 
     container.Register(Component.For<IDatabaseConnectionExecutor>() 
      .ImplementedBy<FailoverDatabaseConnectionExecutor>() 

      .LifestyleTransient()); 

     container.Register(Component.For<IDatabaseConnectionExecutor>() 
      .ImplementedBy<DatabaseConnectionExecutor>() 
      .LifestyleTransient()); 

     var configuration = container.Resolve<IConfigurationRepository>(); 

     container.Kernel.AddHandlerSelector(new DatabaseExecutorHandlerSelector(configuration)); 

     container.Release(configuration); 
    } 
} 

public class DatabaseExecutorHandlerSelector : IHandlerSelector 
{ 
    private readonly IConfigurationRepository configurationRepository; 

    public DatabaseExecutorHandlerSelector(IConfigurationRepository configurationRepository) 
    { 
     this.configurationRepository = configurationRepository; 
    } 

    public bool HasOpinionAbout(string key, Type service) 
    { 
     return service == typeof(IDatabaseConnectionExecutor); 
    } 

    public IHandler SelectHandler(string key, Type service, IHandler[] handlers) 
    { 
     var failoverEnabled = configurationRepository.GetSetting(ConfigurationSettings.BagDatabaseFailoverEnabled,() => false); 

     var implementationToUse = failoverEnabled ? 
      typeof(FailoverDatabaseConnectionExecutor) : 
      typeof(DatabaseConnectionExecutor); 

     return handlers.First(x => x.ComponentModel.Implementation == implementationToUse); 
    } 
} 
0

Le problème ici est que .UsingFactoryMethod() renvoie un composant déjà enregistré, puis essaie de l'enregistrer à nouveau.

Vous pouvez essayer d'ajouter .Named() à votre première et deuxième inscription.

container.Register(Component.For<IDatabaseConnectionExecutor>() 
    .ImplementedBy<FailoverDatabaseConnectionExecutor>() 
    .Named('FailoverExecutor') 
    .LifestyleTransient()); 

container.Register(Component.For<IDatabaseConnectionExecutor>() 
    .ImplementedBy<DatabaseConnectionExecutor>() 
    .Named('NormalExecutor') 
    .LifestyleTransient()); 

container.Register(Component.For<IDatabaseConnectionExecutor>() 
    UsingFactoryMethod(CreateDatabaseConnectionExecutor) 
    .LifestyleTransient() 
    .IsDefault()); 

Dans votre méthode d'usine, vous résolvez ensuite les composants en utilisant le nom.

return configurationRepository.GetSetting(ConfigurationSettings.DatabaseFailoverEnabled,() => false) 
     ? kernel.Resolve<IDatabaseConnectionExecutor>("FailoverExecutor") 
     : kernel.Resolve<IDatabaseConnectionExecutor>("NormalExecutor");