在使用基于约定的注册时,如何使用类型化工厂解析带参数的组件?

How do I use a typed factory to resolve components that take parameters when using convention-based registration?

Windsor 的 docs 说我们可以在 TypedFactory 方法调用中传递参数,这些参数将传递给工厂正在创建的类型的构造函数:

You can also use methods that take parameters from the caller to resolve components. The argument you pass in, will be passed on to the container's resolution pipeline.

使用基于约定的注册时,这是如何工作的?我在验证没有丢失的组件并且我的配置正确时遇到异常,说我想传递给我的构造函数的类型没有被注册。

例如取下面的代码:

public interface IWatcherFactory : IDisposable
{
    IWatcher GetWatcher(ImportTarget importTarget);
}

已注册 container.Register(Component.For<IWatcherFactory>().AsFactory());

public class FolderWatcher : WatcherBase
{
    public FolderWatcher(ImportTarget importTarget, ILogger logger, IClock clock, IFileSystem fileSystem)
        : base(importTarget, logger)
    {
        // ...
    }
}

其中 WatcherBase

public abstract class WatcherBase : IWatcher
{
    public WatcherBase(ImportTarget importTarget, ILogger logger)
    {
        // ...
    }
}

已注册 container.Register(Classes.FromThisAssembly().BasedOn<IWatcher>().WithServiceAllInterfaces().LifestyleTransient());

现在其他问题和答案 say 参数名称需要匹配才能工作,我的已经这样做了。

这正是我遇到的错误:

'FolderWatcher' is waiting for the following dependencies:
- Service 'ImportTarget' which was not registered.

我认为问题在于,由于公约注册,没有任何信息告诉温莎存在对 ImportTarget 的依赖,但我不能确定。

我还有一个组件选择器,如下所示:

public class WatcherFactoryComponentSelector : DefaultTypedFactoryComponentSelector
{
    protected override string GetComponentName(MethodInfo method, object[] arguments)
    {
        var config = arguments.FirstOrDefault() as ImportTarget;
        if (config == null)
        {
            return base.GetComponentName(method, arguments);
        }

        return config.WatcherFullyQualifiedName;
    }
}

这样注册的:container.Register(Component.For<ITypedFactoryComponentSelector>().ImplementedBy<WatcherFactoryComponentSelector>());

看来我需要做的就是向容器注册ImportTarget,这似乎已经完成了工作。

container.Register(Component.For<ImportTarget>());

我还需要更改我的注册方式 IWatcherFactory:

container.Register(Component.For<WatcherFactoryComponentSelector>());
container.Register(Component.For<IWatcherFactory>().AsFactory(cfg => cfg.SelectedWith<WatcherFactoryComponentSelector>()));

免责声明:我不知道这是否是正确的方法,但现在它有效,所以我使用它直到它出现任何其他问题。

所以这个问题涵盖了两个相关但独立的主题。我会依次回答。

为什么温莎抱怨缺少 ImportTarget 依赖项?

这与您注册组件的方式没有任何关系,无论是按照惯例,一个接一个还是(请不要)使用 XML。

请记住,Windsor 在安全方面犯了错误,它不会假设您 只有 会通过使用类型化工厂获得对 FolderWatcher 的依赖方法。 它尝试使用它所拥有的 - 也就是说,它会查看它确实知道的组件,并在意识到它没有 ImportTarget 的组件后,它会生成此消息。

请注意,这不是错误,更多的是警告。它是为了提醒您注意以下事实:如果您试图直接依赖 FolderWatcher,您将会失败。

现在(参考你自己的回答),注册一个 Component.For<ImportTarget>() 会让温莎闭嘴,但我猜(没有看到你的其余代码)一个普通的 ImportTarget ,就像你通过 new ImportTarget() 得到的那样不是很有用,没有正确设置 WatcherFullyQualifiedName。所以这是在掩盖问题而不是解决问题。

将组件选择器连接到类型工厂的正确方法是什么?

这很简单 - 您在这里做对了。 就个人而言,除非您将它用于其他地方的其他工厂,否则我什至不会费心在容器中注册 WatcherFactoryComponentSelector,而是去 .AsFactory(cfg => cfg.SelectedWith(new WatcherFactoryComponentSelector())),但这是一个小问题。