Ninject Factory:根据参数创建适当的类

时间:2017-12-21 11:17:27

标签: c# winforms ninject

我有3个班级:

  1. class SqlQueryService : IQueryService
  2. class FileQueryService : IQueryService
  3. class NCRFileQueryService : FileQueryService
  4. 我创建了一个接口工厂:

    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);
    
    • 如果字符串参数以&#34;数据源&#34;开头?创建SqlQueryService
    • 如果字符串参数以&#34;文件来源&#34;开头。创建FileQueryService
    • 如果字符串参数以&#34; NCR文件源&#34;开头。创建NCRFileQueryService

    可以这样做吗?

    *注意:我的应用程序是一个使用.NET Framework 3.5的winforms应用程序,因为它适用于和OLD窗口

    我使用的Ninject版本是3.2.2.0,Ninject Extensions Factory的版本是3.2.1.0

3 个答案:

答案 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。如何为消费者提供正确的实现,取决于您的应用程序的需求,但有两种选择。

选项1:配置值在启动时已知(在配置DI容器之前)

如果配置值在启动时已知,在配置DI容器之前,这只是意味着您只需要根据该值在容器中注册一个实现。

例如:

Bind(typeof(IQueryService),
    value == "Data Source" ? typeof(SqlQueryService) :
    value == "NCR File Source ? typeof(NCRFileQueryService) :
    value == "File Source" ? typeof(FileQueryService) :
    throw new InvalidOperationException(value));

选项2:配置值在应用程序的生命周期之后已知或可以更改

即使配置值在启动时未修复或已知,仍然没有理由使用工厂抽象并让消费者依赖于该配置值。这可以通过创建代理隐藏在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());