如何配置Startup类构造函数可用的DI服务
本文关键字:构造函数 服务 DI Startup 何配置 配置 | 更新日期: 2023-09-27 18:14:33
当我为ASP.NET Core应用程序创建Web主机时,我可以指定Startup
类,但不能指定实例。
您自己的Startup类的构造函数可以接受通过DI提供的参数。我知道如何在ConfigureServices
中为DI注册服务,但由于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
初始化之前完成(?
尽管Steven的担忧是有效的,您应该注意到它们,但从技术上讲,配置用于解决Startup类的DI容器是可能的。
ASP.NET宿主使用依赖项注入来连接Startup类的实例,还允许我们使用IWebHostBuilder
:上的ConfigureServices
扩展方法将自己的服务添加到该容器中
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容器中(请参阅此部分(。
请注意,您的服务实例将在应用程序容器中再次解析,因为将生成IServiceProvider
的新实例。但是,服务的注册将传递给Startup类中的应用程序IServiceCollection
。
这是一个"鸡还是蛋"的问题:在配置对象图之前,不能让DI容器解析对象图。
但是,您的问题不应该存在,因为正如您应该严格地将容器的注册阶段与解析阶段分开一样(ASP.NET Core强制要求您这样做(,您也应该以同样的方式将注册阶段与加载配置值之前的阶段分开。
这意味着,在容器的注册阶段所需的类不应由容器解析。因为这可能会导致难以追踪的常见问题。
相反,您应该手动创建类。例如:
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.
}
您有一个名为myClass
的MyClass
实例,需要将该实例注入到Startup
类中。我也有类似的要求,经过一些实验,我得出了以下结论。
首先,我们在调用UseStartup
:之前注入myClass
实例
var host = new WebHostBuilder()
// .... other code
.ConfigureServices(services => services.AddSingleton(myClass)) // Inject myClass instance
.UseStartup<WebStartup>()
现在我们需要获取Startup
中的对象(在您的情况下为WebStartup
(。我找不到从构造函数访问它的方法,但这并不重要,因为它可以从启动类"ConfigureServices()
"中访问,如果需要,可以从那里保存到字段或属性(稍后调用Startup.Configure()
(。以下是我的想法:
// This goes into Startup.cs/WebStartup.cs
public virtual void ConfigureServices(IServiceCollection services)
{
var myClass = services.Single(s => s.ServiceType == typeof(MyClass)).ImplementationInstance as MyClass;
if (myClass == null)
throw new InvalidOperationException(nameof(MyClass) + " instance is null");
// Now do all the things
}
我怀疑框架中可能有一些东西可以更容易地检索注入的实例,但如果没有,或者直到我找到它,我可以确认上面的方法非常有效!