【问题标题】:Castle Windsor UsingFactoryMethod with LifestyleTransientCastle Windsor UsingFactoryMethod 和 LifestyleTransient
【发布时间】:2018-02-14 23:03:47
【问题描述】:

考虑在城堡温莎 3.4.0 中的以下实现:

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

框架返回以下异常:

组件后期绑定的Instance FailoverDatabaseConnectionExecutor IDatabaseConnectionExecutor 已被跟踪。工厂 提供组件实例的方法是重用实例,但是 组件的生活方式是瞬态的,这需要新的 每次的实例。在大多数情况下,建议使用工厂方法 不是处理实例的重用,而是选择一种生活方式 这样做是适当的。或者,如果您不希望 温莎追踪来自工厂的物体改变你的 注册到 '.UsingFactoryMethod(yourFactory, managedExternally: 真的)'

这会导致依赖链无法解析,并且我们的控制器上的属性注入为空值。

我们试图实现的是基于配置值ConfigurationSettings.DatabaseFailoverEnabled 的分辨率切换。我们希望这在工厂和底层解析类型上都以瞬时方式发生。

从错误看来这似乎是不可能的,我们的问题是我们如何实现工厂风格的实现,同时仍然在 FailoverDatabaseConnectionExecutorDatabaseConnectionExecutor 上保持瞬态生命周期

编辑:

在花了一些时间进一步调查之后,这似乎是与我的依赖链中的一个对象相关的问题。当其中一个对象实现 IDisposable 时,与 UsingFactoryMethod 结合使用时会发生此错误。

考虑以下(简化的)示例:

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

从 SomeConnectionService 中删除 IDisposable 将正确注入依赖链。

有人知道温莎城堡为什么会这样吗?

【问题讨论】:

  • 请在从工厂方法返回之前尝试 Release() 配置存储库。使用 Castle 时,您应该养成为您 Resolve() 的所有内容显式调用 Release() 的习惯。

标签: c# .net inversion-of-control castle-windsor ioc-container


【解决方案1】:

这里的问题是 .UsingFactoryMethod() 返回一个已经注册的组件,然后尝试再次注册。

您可以尝试将 .Named() 添加到您的第一次和第二次注册中。

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

然后在您的工厂方法中使用名称解析组件。

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

【讨论】:

    【解决方案2】:

    在完成了一些温莎城堡代码之后,似乎很明显在跟踪标记为退役的对象方面存在问题。 More details can be found here

    我们通过改用 IHandlerSelector 克服了这个问题。这允许我们在运行时根据配置设置动态选择依赖项。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-02-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多