如何配置Startup类构造函数可用的DI服务

时间:2016-10-24 15:03:14

标签: c# dependency-injection asp.net-core

当我为ASP.NET Core应用程序创建webhost时,我可以指定Startup类,但不能指定实例。

您自己的Startup类的构造函数可以获取通过DI提供的参数。我知道如何在ConfigureServices内为DI注册服务,但由于这是该类的成员,这些服务不适用于我的启动类的构造函数。

如何注册可用作Startup类的构造函数参数的服务?

原因是我必须提供一个必须在创建webhost之前/之前创建的对象实例,并且我不想以类似全局的样式传递它。

创建IWebHost的代码:

this.host = new WebHostBuilder()
    .UseConfiguration(config)
    .UseKestrel()
    .UseIISIntegration()
    .UseContentRoot(Directory.GetCurrentDirectory())
    .UseStartup<WebStartup>()
log.Debug("Run webhost");
this.host.Start();

WebStartup的构造函数:

public WebStartup(IHostingEnvironment env, MyClass myClass)
{
    var config = new ConfigurationBuilder()
        .SetBasePath(env.ContentRootPath)
        .AddEnvironmentVariables()
        .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
        .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true)
        .Build();
    ...
}

具体来说,如何在此示例中注册MyClass(显然必须在WebStartup实现IWebHost之前完成?)

3 个答案:

答案 0 :(得分:9)

虽然史蒂文的担忧是有效的,你应该注意它们,但技术上可以配置用于解析你的启动类的DI容器。

ASP.NET托管使用依赖注入来连接Startup类的实例,并让我们使用ConfigureServices上的IWebHostBuilder扩展方法将我们自己的服务添加到该容器:

var host = new WebHostBuilder()
    .UseKestrel()
    .UseContentRoot(Directory.GetCurrentDirectory())
    .UseIISIntegration()
    .ConfigureServices(services => services.AddSingleton<IMyService, MyService>())
    .UseStartup<Startup>()
    .Build();

host.Run();

public Startup(IMyService myService)
{
    myService.Test();
}

事实上,UseStartup<WebStartup>所做的就是将其作为IStartup的服务实现添加到托管DI容器(see this)。

请注意,将在应用程序容器中再次解析您的服务实例,因为将构建IServiceProvider的新实例。但是,服务的注册将传递到Startup类中的应用程序IServiceCollection

答案 1 :(得分:7)

这是一个'鸡或蛋'问题:在配置之前,你不能让DI容器解析对象图。

然而,您的问题不应该存在,因为正如您应该严格区分容器的注册阶段和解析阶段(如ASP.NET核心强制执行),您应该将注册阶段与注册阶段分开在您加载配置值之前的阶段。

这意味着在容器的注册阶段您需要的类应该由容器解析。因为这可能导致common problems难以追踪。

相反,您应该手动创建类。例如:

public class Startup
{
    private static MyDependency dependency;

    public Startup(IHostingEnvironment env)
    {
        dependency = new MyDependency();
        var instance = new MyClass(dependency);

        var builder = new ConfigurationBuilder()
            .SetBasePath(instance.ContentRootPath)
            .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
            .AddEnvironmentVariables();
        Configuration = builder.Build();
    }

    public IConfigurationRoot Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        // Register the dependency if it is required by other components.
        services.AddSingleton<MyDependency>(dependency);

        // Add framework services.
        services.AddMvc();

        services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
    }

    // etc.
}

答案 2 :(得分:0)

您有一个名为MyClass的{​​{1}}实例,需要将该实例注入到myClass类中。我有类似的要求,经过一些实验后,我提出了以下建议。

首先,我们在调用Startup之前注入myClass 实例

UseStartup

现在,我们需要掌握var host = new WebHostBuilder() // .... other code .ConfigureServices(services => services.AddSingleton(myClass)) // Inject myClass instance .UseStartup<WebStartup>() 中的对象(在您的情况下为Startup)。我无法找到从构造函数访问它的方法,但这无关紧要,因为可以从启动类的WebStartup中访问它,并在必要时将其保存到字段或属性中。如果需要将其提供给ConfigureServices(),稍后再调用。这是我想出的:

Startup.Configure()

我怀疑框架中可能有某些内容可以更轻松地检索注入的实例,但是如果不能-或直到我找到它,我可以确认以上内容是否完美!