从ServiceCollection获取IConfiguration

38

我正在编写自己的扩展方法,用于ServiceCollection来注册我的模块类型,并且需要访问集合中的IConfiguration实例来注册我的选项。

扩展方法

public static IServiceCollection AddApi(this IServiceCollection services)
{
  // Get configuration from collection
  var configuration = (IConfiguration) services.FirstOrDefault(p => p.ServiceType == typeof(IConfiguration)).ImplementationInstance;

  services.Configure<DatabaseOptions>(configuration.GetSection("Database"));
}

这是从集合中获取 IConfiguration 实例的正确方法吗?还是有更优雅的解决方案?我不想将 IConfiguration 实例添加为该方法的参数。


3
应该直接在需要的地方注入明确的依赖项。否则会使代码具有误导性。虽然如此,如所述并非您想要的,但如果直接将其注入到方法中会更好。 - Nkosi
5
在我看来,从除了组合根(ASP.NET核心的启动类Startup)以外的任何地方访问IConfiguration都是糟糕的设计,因为这意味着配置文件必须具有特定的结构,而且无法更改。相反,我会编写扩展方法,在组合根内部配置配置类,并将IConfiguration对象传递给它,类似于.Configure<MyLibConfig>(Configuration.GetSection("MyLibConfig")。这样,将应用程序从您的组件组合的开发人员可以决定在appsettings.json中将其放置在哪里。 - Tseng
2
或者说,当两个库直接引用IConfiguration并且在配置中具有相同的部分时,您将如何解决冲突?例如,JsonSettings但具有完全不同的结构?唯一的解决方法是让编写它的开发人员选择并传递部分名称给您的扩展方法,该方法通过.Configure<T>设置选项。 - Tseng
对我来说不起作用因为ImplementationInstance为空。只能调用.ImplementationFactory(null)。虽然这样做有效,但显然很不好。 - PhillipM
4个回答

17

IServiceCollection获取IConfiguration,为什么不能只解析依赖项?

IConfiguration configuration = services.BuildServiceProvider().GetService<IConfiguration>();

6
因为这需要构建服务提供商。 - Alexander Trauzzi
根据应用程序的类型和您正在进行的操作,这可能并不是什么大问题。 - bytedev
你如何获取服务? - Doug Kimzey

11
根据评论,我已经将扩展方法更改为以下形式,这样应用程序的构建者可以提供我的选项的配置部分。
public static IServiceCollection AddApi(this IServiceCollection services, IConfiguration databaseConfiguration)
{  
  services.Configure<DatabaseOptions>(databaseConfiguration);
}

从StartUp类中,调用看起来像:

public void ConfigureServices(IServiceCollection services)
{
  services.AddApi(Configuration.GetSection("Database"));
  services.AddMvc();
}

决定以这种方式使用它的大多数是基于这些评论。在开发由许多开发人员使用的组件时,这种方式可能更相关,而不是在应用程序中使用的内部组件。 此外,官方文档指南也解释了同样的方法。

在组合根(ASP.NET Core中的Startup类)之外的任何地方访问IConfiguration我认为是一个糟糕的设计,因为这意味着配置文件必须具有特定的结构,不能改变。相反,我会编写一个扩展方法来配置组合根中的配置类,并传递一个IConfiguration对象,类似于.Configure(Configuration.GetSection("MyLibConfi‌​g"))。 这样,将应用程序从你的组件中组合起来的开发人员可以决定将其放置在appsettings.json中的哪个位置。

或者,当两个库直接引用IConfiguration并且在配置中具有完全不同的结构,例如JsonSettings但具有完全不同的结构时,您将如何解决冲突?只有通过让组合应用程序的开发人员选择和传递部分名称到您的扩展方法来设置选项以通过.Configure解决此问题。


也许在AddApi(...)参数中应该使用IConfigurationSection而不是IConfiguration,以更明确地接受特定的部分而不是整个配置? - Adam Evans

6
我创建了自己的 "服务集合" 类型,它包装了 IServiceCollectionIConfiguration,并且我所有的模块都使用该类型来注册其服务。例如:
public interface IMyServiceCollection
{
    public IServiceCollection Services { get; set; }

    public IConfiguration Configuration { get; set; }
}

public static void AddFooModule(this IMyServiceCollection myServices)
{
    var services = myServices.Services;
    var config = myServices.Configuration;
}

接下来,您需要创建一个扩展方法,并将配置实例作为参数传递,以创建IMyServiceCollection的实现,例如:

public static IMyServiceCollection CreateServiceCollection(this IServiceCollection services, IConfiguration config)
{
    return new MyServiceCollection 
    { 
        Services = services,
        Configuration = config
    };
}

请注意,我们在模块化框架中使用此功能。对于简单的应用程序来说,这是过度设计。
我认为你的解决方案也很好。但是,如果您需要频繁访问IConfiguration实例,则可能会觉得反复在服务集合中搜索它有点乏味。

我在想是否有人会考虑这个选项。我个人会用更流畅的方式编写API,例如 services.WithConfiguration(Configuration).AddServiceA().AddServiceB(); 但是这个想法是一样的。 - Josh Gust

0

文档中提到,您应该要求用户向您提供适当的IConfiguration:

public static IServiceCollection AddApi(this IServiceCollection services, IConfiguration configSection)
{
    services.Configure<DatabaseOptions>(configSection);
    // ...

    return services;
}

您需要安装 Microsoft.Extensions.Options.ConfigurationExtensions 包才能使其正常工作。

请解释一下这个答案相对于其他答案的价值在哪里。看起来Jehof已经在4年前给出了这个答案。 - JHBonarius

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接