簡體   English   中英

Ninject Factory:基於參數創建適當的類

[英]Ninject Factory: Create the appropriate class based on a parameter

我有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);
  • 如果字符串參數以“數據源”開頭,則創建一個SqlQueryService
  • 如果字符串參數以“文件源”開頭,則創建一個FileQueryService
  • 如果字符串參數以“ NCR File Source”開頭,則創建一個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());

請參閱文檔中的將第一個工廠方法參數視為名稱說明符

由於您提供給工廠的值不是用戶提供的值,因此不需要將使用代碼復雜化為:

  • 那個配置值
  • 工廠抽象

使用者不應該依賴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抽象。

謝謝歐文!

我讀了維基,但沒有意識到這種定制。

最后,我決定添加一個類似的類:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM