我有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
答案 0 :(得分:2)
您可以通过创建自定义实例提供程序然后将工厂绑定为:
来执行此操作@GetMapping(API_BASE_PATH + "/flux4")
public String flux4() {
StringBuilder sb = new StringBuilder();
Flux.just("alpha", "bravo", "charlie")
.map(String::toUpperCase)
.flatMap(s -> Flux.fromArray(s.split("")))
.groupBy(String::toString)
.sort(Comparator.comparing(GroupedFlux::key))
.map(group -> group.key() + " => " + group.count() + "; ")
.subscribe(sb::append);
return "flux 4: " + sb.toString();
}
请参阅文档中的Treating the first factory method parameter as a name specifier。
答案 1 :(得分:2)
由于您提供给工厂的值不是用户提供的值,因此不需要将使用代码复杂化:
消费者应该只依赖于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
抽象。
答案 2 :(得分:1)
感谢欧文!
我阅读了维基,但我没有意识到这种定制。
最后我决定添加一个类:
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());