c# - 温莎城堡使用 FactoryMethod 和 LifestyleTransient

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

考虑温莎城堡 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 of component Late bound IDatabaseConnectionExecutor is already being tracked. The factory method providing instances of the component is reusing instances, but the lifestyle of the component is Transient which requires new instance each time. In most cases it is advised for the factory method not to be handling reuse of the instances, but to chose a lifestyle that does that appropriately. Alternatively, if you do not wish for Windsor to track the objects coming from the factory change your regustration to '.UsingFactoryMethod(yourFactory, managedExternally: true)'

这会导致依赖链无法解析,并且 Controller 上的属性注入(inject)出现空值。

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

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

编辑:

花了一些时间进一步调查此问题后,这似乎是与我的依赖链中的一个对象相关的问题。当其中一个对象实现 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 将正确注入(inject)依赖链。

有谁知道为什么温莎城堡会出现这种情况?

最佳答案

这里的问题是 .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");

关于c# - 温莎城堡使用 FactoryMethod 和 LifestyleTransient,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46075494/

相关文章:

.net - 温莎城堡 : How to wire up a component to a factory property rather than method

c# - EmguCV:获取两点之间直线中像素的坐标

c# - 如何获得与 MariaDB 5.5.6 链接的 Visual Studio Community 2017?

c# - 如何使用 .NET enyim 增加 memcached 值?

.net - 从托管 'ref' 内部指针恢复包含 GC 对象

php - 服务定位器、依赖注入(inject)(和容器)和控制反转

c# - 使用 OpenXML 和 XElement 将多个 word 文档合并为一个文档

c# - 尝试创建类型的 Controller 时出错。确保 Controller 有一个无参数的公共(public)构造函数

c# - 如何将 Rijndael 加密与 .Net Core 类库一起使用? (不是 .Net 框架)

php - Laravel - 调用未定义的方法 TrainingFacade::save()