C# Ninject工厂:基于参数创建适当的类

C# Ninject工厂:基于参数创建适当的类,c#,winforms,ninject,C#,Winforms,Ninject,我有三门课: 类SqlQueryService:IQueryService 类FileQueryService:IQueryService 类NCRFileQueryService:FileQueryService 我创建了一个接口工厂: public interface IQueryServiceFactory { IQueryService Create(string connection); } 在应用程序模块中: Bind(typeof(IQueryService)).To(t

我有三门课:

类SqlQueryService:IQueryService 类FileQueryService:IQueryService 类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();
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());
如果字符串参数以数据源开头,请创建SqlQueryService 如果字符串参数以文件源开头,请创建一个FileQueryService 如果字符串参数以NCR文件源开头,请创建NCRFileQueryService 有可能吗

*注意:我的应用程序是带有.NET Framework 3.5的winforms应用程序,因为它适用于和旧windows


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

您可以通过创建自定义实例提供程序,然后绑定工厂来实现这一点,如:

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

请参阅文档中的。

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

this.Bind<IQueryServiceFactory>()
    .ToFactory(() => new UseFirstArgumentAsNameInstanceProvider());
请参阅文档中的。

谢谢欧文

我读过维基,但我没有意识到这种定制

最后,我决定添加一个类,如:

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();
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());
谢谢你,欧文

我读过维基,但我没有意识到这种定制

最后,我决定添加一个类,如:

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();
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());

由于您提供给工厂的值不是用户提供的值,因此无需使用以下代码使消费代码复杂化:

该配置值 工厂抽象 消费者不应该依赖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抽象。

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

该配置值 工厂抽象 消费者不应该依赖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抽象。

您的配置值\u configuration.SelectedTPV.Connection是一个在运行时不会更改的固定值,还是在应用程序运行时该值实际会更改?无论如何,你都不需要工厂。删除IQueryServiceFactory将改进您的设计。配置值是在网站上设置的,可以是多个。然后,用户使用其凭据下载应用程序登录,然后应用程序将连接到api以获取配置值。然后用户可以从下拉列表中进行选择。如果我删除IQueryServiceFactory,我如何根据配置值创建具体类?您的配置值\u configuration.SelectedTPV.Connection是一个在运行时不会更改的固定值,还是在应用程序运行时该值实际上会更改?无论如何,你都不需要工厂。重新
移动IQueryServiceFactory将改进您的设计。配置值是在网站上设置的,可以是多个。然后,用户使用其凭据下载应用程序登录,然后应用程序将连接到api以获取配置值。然后用户可以从下拉列表中进行选择。如果我删除IQueryServiceFactory,我如何根据配置值创建具体类?好的,我想我理解你的观点,因此现在只需将代理类注册为IQueryService,并在consumer类中将其注入为其他服务,是的,我认为这更干净:-,那么什么时候使用工厂呢?我发现工厂的使用率很低。如果这个值来自用户,那么对于工厂可能会有更多的用途,但是在创建工厂之前,您应该首先考虑一个中介/委派方法。好的,我想我理解您的观点,因此,现在只需将代理类注册为IQueService,然后在消费类中登记它作为其他服务,是的,我认为这更干净:-,那么什么时候使用工厂?我发现工厂的使用率很低。如果该值来自用户,则可能对工厂有更多的使用,但即使如此,在创建工厂之前,您应该首先考虑中介/委派方法。