我有3个班:
class SqlQueryService : IQueryService
class FileQueryService : IQueryService
class NCRFileQueryService : FileQueryService
我创建了一个接口工厂:
public interface IQueryServiceFactory
{
IQueryService Create(string connection);
}
在应用程序模块中:
Bind(typeof(IQueryService)).To(typeof(SqlQueryService)).Named("Data Source");
Bind(typeof(IQueryService)).To(typeof(NCRFileQueryService)).Named("NCR File Source");
Bind(typeof(IQueryService)).To(typeof(FileQueryService)).Named("File Source");
Bind<IQueryServiceFactory>().ToFactory();
在我的应用程序中,我想基于如下参数创建三个类之一的实例:
IQueryService queryService =
_queryServiceFactory.Create(_configuration.SelectedTPV.Connection);
SqlQueryService
FileQueryService
NCRFileQueryService
有可能这样做吗?
*注意:我的应用程序是一个带有.NET Framework 3.5的winforms应用程序,因为它适用于和OLD窗口
我使用的Ninject版本是3.2.2.0,Ninject Extensions Factory的版本是3.2.1.0
您可以通过创建自定义实例提供程序然后绑定工厂来执行此操作,如:
this.Bind<IQueryServiceFactory>()
.ToFactory(() => new UseFirstArgumentAsNameInstanceProvider());
请参阅文档中的Treating the first factory method parameter as a name specifier。
由于您提供给工厂的值不是用户提供的值,因此不需要将使用代码复杂化:
消费者应该依赖于IQueryServiceFactory
,而不是依赖于IQueryService
。如何为消费者提供正确的实现,取决于您的应用程序的需求,但有两种选择。
如果在启动时已知配置值,则在配置DI容器之前,这只是意味着您只需基于该值在容器中注册一个实现。
例如:
Bind(typeof(IQueryService),
value == "Data Source" ? typeof(SqlQueryService) :
value == "NCR File Source ? typeof(NCRFileQueryService) :
value == "File Source" ? typeof(FileQueryService) :
throw new InvalidOperationException(value));
即使配置值在启动时未修复或已知,仍然没有理由使用工厂抽象并让消费者依赖于该配置值。这可以通过创建代理隐藏在IQueryService
抽象背后:
public class ConfigurationSelectorQueryServiceProxy : IQueryService
{
private readonly IQueryService a;
private readonly IQueryService b;
private readonly IQueryService c;
public ConfigurationSelectorQueryServiceProxy(
SqlQueryService a, NCRFileQueryService b, FileQueryService c) {
this.a = a;
this.b = b;
this.c = c;
}
// IQueryService methods. Forward the call to one of the wrapped services
public object SomeMethod(object args) => GetService().SomeMethod(args);
// Helper methods
private IQueryService GetService() =>
// Read configuration value
GetService(_configuration.SelectedTPV.Connection);
private IQueryService GetService(string value) =>
value == "Data Source" ? (this.a :
value == "NCR File Source ? this.b :
value == "File Source" ? this.c :
throw new InvalidOperationException(value);
}
这个ConfigurationSelectorQueryServiceProxy
代理实现可以注册为IQueryService
并注入消费者。这样,消费者不必知道选择正确实现的复杂性。他们可以简单地使用IQueryService
抽象。
谢谢欧文!
我读了维基,但我没有意识到这种定制。
最后我决定添加一个类,如:
public class QueryServiceInstanceProvider : StandardInstanceProvider
{
protected override string GetName(System.Reflection.MethodInfo methodInfo, object[] arguments)
{
string connection = arguments[0].ToString();
return connection.Split('=')[0];
}
protected override Ninject.Parameters.IConstructorArgument[] GetConstructorArguments(System.Reflection.MethodInfo methodInfo, object[] arguments)
{
return base.GetConstructorArguments(methodInfo, arguments).Skip(1).ToArray();
}
}
并在应用程序模块中:
Bind(typeof(IQueryService)).To(typeof(SqlQueryService)).Named("Data Source");
Bind(typeof(IQueryService)).To(typeof(NCRFileQueryService)).Named("NCR File Source");
Bind(typeof(IQueryService)).To(typeof(FileQueryService)).Named("File Source");
Bind<IQueryServiceFactory>().ToFactory(() => new QueryServiceInstanceProvider());