Startupclass的Configure()方法中服务的方法注入和构造函数注入有区别吗?
Is there a difference between method injection or constructor injection of services in the Configure() method of the Startup class?
根据the docs我们可以将以下服务注入启动class:
- IWebHostEnvironment
- IHostEnvironment
- 我配置
但是我们也可以在Configure()
方法中使用方法注入来解析这些服务:
Additional services, such as IWebHostEnvironment, ILoggerFactory, or anything defined in ConfigureServices, can be specified in the Configure method signature. These services are injected if they're available.
我使用哪种注入变体有区别吗?
具体来说,在构造函数中解析 IWebHostEnvironment
然后通过私有字段在 Configure()
方法中访问它与将其作为方法参数注入有区别吗?
public class Startup
{
private readonly IWebHostEnvironment env;
public Startup(IWebHostEnvironment env)
{
this.env = env;
}
public void Configure(IApplicationBuilder app)
{
if (this.env.IsDevelopment())
[...]
}
}
对比
public class Startup
{
public Startup() {}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
[...]
}
}
一般来说,构造函数和方法 DI 可以是偏好,也可以是基于使用它而不是另一个的框架的要求。在 asp.net core
中(以及 .net core
通常),建议使用构造函数注入,因为它清楚地说明了依赖关系。这比方法注入更有意义。但是在某些情况下,您必须使用方法注入,因为依赖项只能在 调用 方法时可用,或者至少在调用时不可用构建 classes/services 的时间。有时注入的服务仅用于特定的方法调用,在这种情况下将使用方法注入。在某些情况下,服务实例是单例的,但它的方法调用需要一些范围内的服务,因此将使用方法注入来代替。该场景的一个示例是使用 convention-based middleware
时。基于约定的中间件是单例的,在应用程序启动时创建一次,而不是为每个请求创建一次。所以所有范围内的服务都必须注入到 Invoke
或 InvokeAsync
方法中。然而,基于工厂的中间件可以将其作用域服务注入到构造函数中,因为它们可以根据请求创建(注册为作用域或瞬态)。
如 Startup
class 的情况。你可以在 Startup
的构造函数中注入一些内置服务,例如 IConfiguration
, IWebHostEnvironment
, ... 通常很多服务在 ConfigureServices
之前可用(之前注册过)注入到 Startup
的构造函数中。这意味着在 ConfigureServices
中注册的所有服务都不可 用于注入 Startup
的构造函数。但是,您可以将所有已注册的服务(包括内置服务和您自己的服务)注入到方法 Configure
中。请注意,注入 Configure
的服务应该是单例和瞬态的,对于作用域服务,使用它们时可能会出现问题。这实际上取决于您使用的具体服务。
这是一个示例,表明无法将您自己的服务(在 ConfigureServices
中注册)注入到 Startup
的构造函数中:
public interface ISomeService {}
public class SomeService : ISomeService {}
public class Startup {
//will not work because at this time, the ISomeService has not been registered yet
public Startup(ISomeService someService){
//an error will be thrown complaining about not being able to
//resolve the ISomeService
}
public void ConfigureServices(IServiceCollection services){
//...
services.AddSingleton<ISomeService,SomeService>();
//...
}
//this works because at this time the ConfigureServices was called
//the services container has been built with your registered ISomeService
public void Configure(IApplicationBuilder app, ISomeService someService){
//...
}
}
根据the docs我们可以将以下服务注入启动class:
- IWebHostEnvironment
- IHostEnvironment
- 我配置
但是我们也可以在Configure()
方法中使用方法注入来解析这些服务:
Additional services, such as IWebHostEnvironment, ILoggerFactory, or anything defined in ConfigureServices, can be specified in the Configure method signature. These services are injected if they're available.
我使用哪种注入变体有区别吗?
具体来说,在构造函数中解析 IWebHostEnvironment
然后通过私有字段在 Configure()
方法中访问它与将其作为方法参数注入有区别吗?
public class Startup
{
private readonly IWebHostEnvironment env;
public Startup(IWebHostEnvironment env)
{
this.env = env;
}
public void Configure(IApplicationBuilder app)
{
if (this.env.IsDevelopment())
[...]
}
}
对比
public class Startup
{
public Startup() {}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
[...]
}
}
一般来说,构造函数和方法 DI 可以是偏好,也可以是基于使用它而不是另一个的框架的要求。在 asp.net core
中(以及 .net core
通常),建议使用构造函数注入,因为它清楚地说明了依赖关系。这比方法注入更有意义。但是在某些情况下,您必须使用方法注入,因为依赖项只能在 调用 方法时可用,或者至少在调用时不可用构建 classes/services 的时间。有时注入的服务仅用于特定的方法调用,在这种情况下将使用方法注入。在某些情况下,服务实例是单例的,但它的方法调用需要一些范围内的服务,因此将使用方法注入来代替。该场景的一个示例是使用 convention-based middleware
时。基于约定的中间件是单例的,在应用程序启动时创建一次,而不是为每个请求创建一次。所以所有范围内的服务都必须注入到 Invoke
或 InvokeAsync
方法中。然而,基于工厂的中间件可以将其作用域服务注入到构造函数中,因为它们可以根据请求创建(注册为作用域或瞬态)。
如 Startup
class 的情况。你可以在 Startup
的构造函数中注入一些内置服务,例如 IConfiguration
, IWebHostEnvironment
, ... 通常很多服务在 ConfigureServices
之前可用(之前注册过)注入到 Startup
的构造函数中。这意味着在 ConfigureServices
中注册的所有服务都不可 用于注入 Startup
的构造函数。但是,您可以将所有已注册的服务(包括内置服务和您自己的服务)注入到方法 Configure
中。请注意,注入 Configure
的服务应该是单例和瞬态的,对于作用域服务,使用它们时可能会出现问题。这实际上取决于您使用的具体服务。
这是一个示例,表明无法将您自己的服务(在 ConfigureServices
中注册)注入到 Startup
的构造函数中:
public interface ISomeService {}
public class SomeService : ISomeService {}
public class Startup {
//will not work because at this time, the ISomeService has not been registered yet
public Startup(ISomeService someService){
//an error will be thrown complaining about not being able to
//resolve the ISomeService
}
public void ConfigureServices(IServiceCollection services){
//...
services.AddSingleton<ISomeService,SomeService>();
//...
}
//this works because at this time the ConfigureServices was called
//the services container has been built with your registered ISomeService
public void Configure(IApplicationBuilder app, ISomeService someService){
//...
}
}