在使用基于约定的注册时,如何使用类型化工厂解析带参数的组件?
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()))
,但这是一个小问题。
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()))
,但这是一个小问题。