如何使用ASP.NET Core MVC内置依赖注入框架手动解析类型?
设置容器很容易:
public void ConfigureServices(IServiceCollection services)
{
// ...
services.AddTransient<ISomeService, SomeConcreteService>();
}
但是如何在不进行注射的情况下解决ISomeService
?例如,我想这样做:
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<IFooService>()
(为using
添加Microsoft.Extensions.DependencyInjection
)。
运行时可以在Startup
类的构造函数中注入服务,例如IHostingEnvironment
,IConfiguration
和IServiceProvider
。请注意,此服务提供商是由托管层构建的实例,仅包含启动应用程序的服务。
服务也可以用Configure()
方法注入。您可以在IApplicationBuilder
参数后添加任意参数列表。您也可以注入自己在ConfigureServices()
方法中注册的服务,它们将从应用程序服务提供商而不是托管服务提供商处解析。
public void Configure(IApplicationBuilder app, IFooService fooService)
{
// ...
}
然而,ConfigureServices()
方法不允许注入服务,它只接受IServiceCollection
参数。这是配置应用程序依赖项注入容器的方法。您可以在此处使用在启动构造函数中注入的服务。例如:
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
// Use Configuration here
}
如果要手动解析服务,可以让运行时在构造函数中注入IServiceProvider
实例,或者使用ApplicationServices
在IApplicationBuilder
方法中提供的Configure()
:
public Startup(IServiceProvider serviceProvider)
{
var hostingEnv = serviceProvider.GetService<IHostingEnvironment>();
}
要么
public void Configure(IApplicationBuilder app)
{
var serviceProvider = app.ApplicationServices;
var hostingEnv = serviceProvider.GetService<IHostingEnvironment>();
}
但是,如果需要在ConfigureServices()
方法中解析服务,则需要采用不同的方法。您可以从IServiceProvider
实例构建一个中间IServiceCollection
,其中包含在此之前注册的服务:
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IFooService, FooService>();
// Build the intermediate service provider
var sp = services.BuildServiceProvider();
var fooService = sp.GetService<IFooService>();
}
你需要Microsoft.Extensions.DependencyInjection
包。
请注意:
通常,您不应该在ConfigureServices()
方法中解析服务,因为这实际上是您配置应用程序服务的地方。有时你只需要访问一些IOptions<MyOptions>
实例。您可以通过将IConfiguration
实例中的值绑定到MyOptions
的实例(这实际上是选项框架的功能)来实现此目的:
public void ConfigureServices(IServiceCollection services)
{
var myOptions = new MyOptions();
Configuration.GetSection("SomeSection").Bind(myOptions);
}
手动解决服务(也称服务定位器)通常是known as an anti-pattern。虽然它有用例(对于框架和/或基础架构层),但您应该尽可能地避免使用它。
手动解析实例涉及使用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 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(ConfigureServices)
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();
}
如果您只需要解析一个依赖项以将其传递给您正在注册的另一个依赖项的构造函数,则可以执行此操作。
假设您有一个接受字符串和ISomeService的服务。
public class AnotherService : IAnotherService
{
public AnotherService(ISomeService someService, string serviceUrl)
{
...
}
}
当你在Startup.cs中注册这个时,你需要这样做:
services.AddScoped<IAnotherService>(ctx =>
new AnotherService(ctx.GetService<ISomeService>(), "https://someservice.com/")
);
您可以通过这种方式在AuthorizeAttribute等属性中注入依赖项
var someservice = (ISomeService)context.HttpContext.RequestServices.GetService(typeof(ISomeService));
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddDbContext<ConfigurationRepository>(options =>
options.UseSqlServer(Configuration.GetConnectionString("SqlConnectionString")));
services.AddScoped<IConfigurationBL, ConfigurationBL>();
services.AddScoped<IConfigurationRepository, ConfigurationRepository>();
}