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

问题描述 投票:1回答:3

我有3个班:

  1. class SqlQueryService : IQueryService
  2. class FileQueryService : IQueryService
  3. 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);
  • 如果字符串参数以“Data Source”开头,则创建一个SqlQueryService
  • 如果字符串参数以“File Source”开头,则创建一个FileQueryService
  • 如果字符串参数以“NCR File Source”开头,则创建一个NCRFileQueryService

有可能这样做吗?

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

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

c# winforms ninject
3个回答
2
投票

您可以通过创建自定义实例提供程序然后绑定工厂来执行此操作,如:

this.Bind<IQueryServiceFactory>()
    .ToFactory(() => new UseFirstArgumentAsNameInstanceProvider());

请参阅文档中的Treating the first factory method parameter as a name specifier


2
投票

由于您提供给工厂的值不是用户提供的值,因此不需要将使用代码复杂化:

  • 那个配置值
  • 工厂抽象

消费者应该依赖于IQueryServiceFactory,而不是依赖于IQueryService。如何为消费者提供正确的实现,取决于您的应用程序的需求,但有两种选择。

Option 1: The configuration value is known at start-up time (before the DI Container is configured)

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

例如:

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

Option 2: The configuration value is known after, or can change during the lifetime of the application

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


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());
© www.soinside.com 2019 - 2024. All rights reserved.