如何使用ASP.NET Core MVC内置依赖项注入框架手动解析类型?
设置容器非常容易:
public void ConfigureServices(IServiceCollection services)
{
// ...
services.AddTransient<ISomeService, SomeConcreteService>();
}
但是如何在不执行注入的情况下解析异构服务
呢?例如,我想这样做:
ISomeService service = services.Resolve<ISomeService>();
IServiceCollection
中没有这样的方法。
IServiceCollection
接口用于构建依赖注入容器。在完全构建之后,它将被组合到一个IServiceProvider
实例中,您可以使用该实例来解析服务。您可以将IServiceProvider
注入到任何类中。IApplicationBuilder
和HttpContext
类也可以分别通过它们的ApplicationServices
或RequestServices
属性提供服务提供者。
IServiceProvider
定义用于解析服务的GetService(Type Type)
方法:
var service = (IFooService)serviceProvider.GetService(typeof(IFooService));
还有几种方便的扩展方法可用,例如ServiceProvider.GetService
(使用为Microsoft.Extensions.DependencyInjection
添加一个)。
运行库的宿主服务提供程序可以将某些服务注入Startup
类的构造函数中,例如Iconfiguration
,IWebHostEnvironment
(IHostingEnvironment
-3.0之前版本中的IloggerFactory
和IServiceProvider
。请注意,后者是由宿主层构建的实例,只包含启动应用程序所需的基本服务。
ConfigureReservices()
方法不允许注入服务,它只接受IServiceCollection
参数。这是有意义的,因为configureReservices()
是注册应用程序所需服务的地方。但是,您可以在这里使用注入到初创企业的构造函数中的服务,例如:
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
// Use Configuration here
}
然后,在configureReservices()
中注册的任何服务都可以注入到configure()
方法中;可以在IApplicationBuilder
参数后添加任意数量的服务:
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IFooService>();
}
public void Configure(IApplicationBuilder app, IFooService fooService)
{
fooService.Bar();
}
如果需要手动解析服务,最好使用IApplicationBuilder
在Configure()
方法中提供的ApplicationServices
:
public void Configure(IApplicationBuilder app)
{
var serviceProvider = app.ApplicationServices;
var hostingEnv = serviceProvider.GetService<IHostingEnvironment>();
}
可以在startup
类的构造函数中传递并直接使用IServiceProvider
,但如上所述,这将包含有限的服务子集,因此其实用性有限:
public Startup(IServiceProvider serviceProvider)
{
var hostingEnv = serviceProvider.GetService<IWebHostEnvironment>();
}
如果必须在configureReservices()
方法中解析服务,则需要使用不同的方法。您可以从IServiceCollection
实例构建中间IServiceProvider
,该实例包含到那时为止已经注册的服务:
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IFooService, FooService>();
// Build the intermediate service provider
var sp = services.BuildServiceProvider();
// This will succeed.
var fooService = sp.GetService<IFooService>();
// This will fail (return null), as IBarService hasn't been registered yet.
var barService = sp.GetService<IBarService>();
}
请注意:通常,您应该避免在configureReservices()
方法中解析服务,因为这实际上是您配置应用程序服务的地方。有时您只需要访问ioptions
实例。您可以通过将Iconfiguration
实例中的值绑定到MyOptions
实例(这实际上是options框架所做的工作)来实现这一点:
public void ConfigureServices(IServiceCollection services)
{
var myOptions = new MyOptions();
Configuration.GetSection("SomeSection").Bind(myOptions);
}
手动解析服务(又称服务定位器)通常被认为是一种反模式。虽然它有它的用例(对于框架和/或基础结构层),但是您应该尽可能地避免它。
手动解析实例涉及使用IServiceProvider
接口:
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IMyService, MyService>();
var serviceProvider = services.BuildServiceProvider();
var service = serviceProvider.GetService<IMyService>();
}
public void Configure(
IApplicationBuilder application,
IServiceProvider serviceProvider)
{
// By type.
var service1 = (MyService)serviceProvider.GetService(typeof(MyService));
// Using extension method.
var service2 = serviceProvider.GetService<MyService>();
// ...
}
public void Configure(
IApplicationBuilder application,
IWebHostEnvironment webHostEnvironment)
{
application.ApplicationServices.GetService<MyService>();
}
某些类型可以作为方法参数注入:
public class Startup
{
public Startup(
IHostingEnvironment hostingEnvironment,
ILoggerFactory loggerFactory)
{
}
public void ConfigureServices(
IServiceCollection services)
{
}
public void Configure(
IApplicationBuilder application,
IHostingEnvironment hostingEnvironment,
IServiceProvider serviceProvider,
ILoggerFactory loggerfactory,
IApplicationLifetime applicationLifetime)
{
}
}
[HttpGet("/some-action")]
public string SomeAction([FromServices] IMyService myService) => "Hello";
如果您使用模板生成应用程序,则在startup
类中将有如下内容:
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddApplicationInsightsTelemetry(Configuration);
services.AddMvc();
}
然后可以在那里添加依赖项,例如:
services.AddTransient<ITestService, TestService>();
如果要访问控制器上的ITestService
,可以在构造函数上添加IServiceProvider
,它将被注入:
public HomeController(IServiceProvider serviceProvider)
然后您可以解析您添加的服务:
var service = serviceProvider.GetService<ITestService>();
请注意,要使用泛型版本,必须将命名空间与扩展一起包含:
using Microsoft.Extensions.DependencyInjection;
iTestService.cs
public interface ITestService
{
int GenerateRandom();
}
TestService.cs
public class TestService : ITestService
{
public int GenerateRandom()
{
return 4;
}
}
startup.cs(ConfigureReservices)
public void ConfigureServices(IServiceCollection services)
{
services.AddApplicationInsightsTelemetry(Configuration);
services.AddMvc();
services.AddTransient<ITestService, TestService>();
}
HomeController.cs
using Microsoft.Extensions.DependencyInjection;
namespace Core.Controllers
{
public class HomeController : Controller
{
public HomeController(IServiceProvider serviceProvider)
{
var service = serviceProvider.GetService<ITestService>();
int rnd = service.GenerateRandom();
}