关于c#:使用ASP.NET Core DI解析实例

Resolving instances with ASP.NET Core DI

如何使用ASP.NET Core MVC内置依赖注入框架手动解析类型?

设置容器很容易:

1
2
3
4
5
6
public void ConfigureServices(IServiceCollection services)
{
    // ...

    services.AddTransient<ISomeService, SomeConcreteService>();
}

但是如何在不执行注射的情况下解析ISomeService? 例如,我想这样做:

1
ISomeService service = services.Resolve<ISomeService>();

IServiceCollection中没有这样的方法。


IServiceCollection接口用于构建依赖项注入容器。在完全构建之后,它将组成一个IServiceProvider实例,您可以使用它来解析服务。您可以将IServiceProvider注入任何类。 IApplicationBuilderHttpContext类也可以分别通过ApplicationServicesRequestServices属性提供服务提供者。

IServiceProvider定义了一个解析服务的GetService(Type type)方法:

1
var service = (IFooService)serviceProvider.GetService(typeof(IFooService));

还有一些便利扩展方法可用,例如serviceProvider.GetService()(为Microsoft.Extensions.DependencyInjection添加using)。

解析启动类中的服务

注入依赖项

运行时可以在Startup类的构造函数中注入服务,例如IHostingEnvironmentIConfigurationIServiceProvider。请注意,此服务提供商是由托管层构建的实例,仅包含启动应用程序的服务。

也可以在Configure()方法中注入服务。您可以在IApplicationBuilder参数后添加任意参数列表。您也可以注入自己在ConfigureServices()方法中注册的服务,它们将从应用程序服务提供商而不是托管服务提供商处解析。

1
2
3
4
public void Configure(IApplicationBuilder app, IFooService fooService)
{
   // ...
}

但是ConfigureServices()方法不允许注入服务,它只接受IServiceCollection参数。这是配置应用程序依赖项注入容器的方法。您可以在此处使用在启动构造函数中注入的服务。例如:

1
2
3
4
5
6
7
8
9
10
11
public Startup(IConfiguration configuration)
{
    Configuration = configuration;
}

public IConfiguration Configuration { get; }

public void ConfigureServices(IServiceCollection services)
{
    // Use Configuration here
}

手动解决依赖关系

如果要手动解析服务,可以让运行时在构造函数中注入IServiceProvider实例,或者使用Configure()方法中IApplicationBuilder提供的ApplicationServices

1
2
3
4
public Startup(IServiceProvider serviceProvider)
{
    var hostingEnv = serviceProvider.GetService<IHostingEnvironment>();
}

要么

1
2
3
4
5
public void Configure(IApplicationBuilder app)
{
    var serviceProvider = app.ApplicationServices;
    var hostingEnv = serviceProvider.GetService<IHostingEnvironment>();
}

但是,如果需要在ConfigureServices()方法中解析服务,则需要采用不同的方法。您可以从IServiceCollection实例构建一个中间IServiceProvider,该实例包含在此之前注册的服务:

1
2
3
4
5
6
7
8
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实例。您可以通过将IConfiguration实例中的值绑定到MyOptions的实例(这实际上是选项框架的作用)来实现此目的:

1
2
3
4
5
public void ConfigureServices(IServiceCollection services)
{
    var myOptions = new MyOptions();
    Configuration.GetSection("SomeSection").Bind(myOptions);
}

手动解析服务(也称为服务定位器)通常被称为反模式。虽然它有用例(对于框架和/或基础架构层),但您应该尽可能地避免使用它。


手动解析实例涉及使用IServiceProvider接口:

解决Startup.ConfigureServices中的依赖关系

1
2
3
4
5
6
7
public void ConfigureServices(IServiceCollection services)
{
    services.AddTransient<IMyService, MyService>();

    var serviceProvider = services.BuildServiceProvider();
    var service = serviceProvider.GetService<IMyService>();
}

解决Startup.Configure中的依赖项

1
2
3
4
5
6
7
8
9
10
11
12
public void Configure(
    IApplicationBuilder application,
    IServiceProvider serviceProvider)
{
    // By type.
    var service1 = (MyService)serviceProvider.GetService(typeof(MyService));

    // Using extension method.
    var service2 = serviceProvider.GetService<MyService>();

    // ...
}

使用运行时注入服务

某些类型可以作为方法参数注入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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)
    {
    }
}

解决控制器操作中的依赖关系

1
2
[HttpGet("/some-action")]
public string SomeAction([FromServices] IMyService myService) =>"Hello";


如果您使用模板生成应用程序,那么您将在Startup类上获得类似的内容:

1
2
3
4
5
6
7
public void ConfigureServices(IServiceCollection services)
{
    // Add framework services.
    services.AddApplicationInsightsTelemetry(Configuration);

    services.AddMvc();
}

然后,您可以在那里添加依赖项,例如:

1
services.AddTransient<ITestService, TestService>();

如果要在控制器上访问ITestService,可以在构造函数上添加IServiceProvider,它将被注入:

1
public HomeController(IServiceProvider serviceProvider)

然后,您可以解决您添加的服务:

1
var service = serviceProvider.GetService<ITestService>();

请注意,要使用通用版本,您必须包含带有扩展名的命名空间:

1
using Microsoft.Extensions.DependencyInjection;

ITestService.cs

1
2
3
4
public interface ITestService
{
    int GenerateRandom();
}

TestService.cs

1
2
3
4
5
6
7
public class TestService : ITestService
{
    public int GenerateRandom()
    {
        return 4;
    }
}

Startup.cs(ConfigureServices)

1
2
3
4
5
6
7
public void ConfigureServices(IServiceCollection services)
{
    services.AddApplicationInsightsTelemetry(Configuration);
    services.AddMvc();

    services.AddTransient<ITestService, TestService>();
}

HomeController.cs

1
2
3
4
5
6
7
8
9
10
11
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的服务。

1
2
3
4
5
6
7
public class AnotherService : IAnotherService
{
    public AnotherService(ISomeService someService, string serviceUrl)
    {
        ...
    }
}

当你在Startup.cs中注册这个时,你需要这样做:

1
2
3
services.AddScoped<IAnotherService>(ctx =>
      new AnotherService(ctx.GetService<ISomeService>(),"https://someservice.com/")
);


您可以通过这种方式在AuthorizeAttribute等属性中注入依赖项

1
var someservice = (ISomeService)context.HttpContext.RequestServices.GetService(typeof(ISomeService));

1
2
3
4
5
6
7
8
9
10
public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();

    services.AddDbContext<ConfigurationRepository>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("SqlConnectionString")));

    services.AddScoped<IConfigurationBL, ConfigurationBL>();
    services.AddScoped<IConfigurationRepository, ConfigurationRepository>();
}