用ASP解析实例。NET Core DI来自ConfigureServices

我如何手动解析一个类型使用ASP。NET核心MVC内置依赖注入框架?

设置容器非常简单:

public void ConfigureServices(IServiceCollection services)
{
// ...


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

但是我如何解决ISomeService不执行注入?例如,我想这样做:

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

IServiceCollection中没有这样的方法。

448021 次浏览

手动解析实例需要使用IServiceProvider接口:

在启动时解决依赖关系。ConfigureServices

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>();


// ...
}

解决启动中的依赖关系。在ASP中配置。NET Core 3

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";

IServiceCollection接口用于依赖注入容器建筑。在它完全构建之后,它被组合成一个IServiceProvider实例,您可以使用它来解析服务。可以将IServiceProvider注入到任何类中。IApplicationBuilderHttpContext类也可以通过它们各自的IServiceProvider0或IServiceProvider1属性提供服务提供者。

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

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

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

解析启动类内部的服务

注入依赖关系

运行时的托管服务提供者可以将某些服务注入到Startup类的构造函数中,例如IConfigurationIWebHostEnvironment(3.0之前的版本是IHostingEnvironment), ILoggerFactoryIServiceProvider。注意,后者是由宿主层和仅包含启动应用程序所需的基本服务构建的实例

ConfigureServices()方法不允许注入服务,它只接受IServiceCollection参数。这是有意义的,因为ConfigureServices()是您注册应用程序所需服务的位置。但是你可以在这里使用注入到启动构造函数中的服务,例如:

public Startup(IConfiguration configuration)
{
Configuration = configuration;
}


public IConfiguration Configuration { get; }


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

ConfigureServices()中注册的任何服务都可以被注入到Configure()方法中;你可以在IApplicationBuilder参数后添加任意数量的服务:

public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IFooService>();
}


public void Configure(IApplicationBuilder app, IFooService fooService)
{
fooService.Bar();
}

手动解析依赖项

如果你需要手动解析服务,你最好使用Configure()方法中IApplicationBuilder提供的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>();
}

如果必须在ConfigureServices()方法中解析服务,则需要使用不同的方法。你可以从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>();
}

< >强请注意: 通常,您应该避免在ConfigureServices()方法中解析服务,因为这实际上是应用程序服务配置的位置。有时您只需要访问IOptions<MyOptions>实例。您可以通过将IConfiguration实例的值绑定到MyOptions实例(这实际上是选项框架所做的)来实现这一点:

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

或者对AddSingleton/AddScoped/AddTransient使用重载:

// Works for AddScoped and AddTransient as well
services.AddSingleton<IBarService>(sp =>
{
var fooService = sp.GetRequiredService<IFooService>();
return new BarService(fooService);
}

手动解析服务(又名服务定位器)是通常被认为是反模式。虽然它有它的用例(用于框架和/或基础设施层),但您应该尽可能避免使用它。

如果你用模板生成一个应用程序,你会在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();
}
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();


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


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

您可以通过这种方式在诸如AuthorizeAttribute之类的属性中注入依赖项

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

如果您只是为了将一个依赖项传递给正在注册的另一个依赖项的构造函数而解析它,那么您可以这样做。

假设您有一个接受字符串和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/")
);

我知道这是一个老问题,但我很惊讶,一个相当明显和恶心的黑客没有在这里。

您可以利用定义自己的ctor函数的能力,在定义服务时从服务中获取必要的值……显然,每次请求服务时都会运行这个命令,除非你显式地删除/清除并重新添加这个服务在开发主体的第一个构造中的定义。

这种方法的优点是不需要您在服务配置期间构建或使用服务树。您仍然在定义如何配置服务。

public void ConfigureServices(IServiceCollection services)
{
//Prey this doesn't get GC'd or promote to a static class var
string? somevalue = null;


services.AddSingleton<IServiceINeedToUse, ServiceINeedToUse>(scope => {
//create service you need
var service = new ServiceINeedToUse(scope.GetService<IDependantService>())
//get the values you need
somevalue = somevalue ?? service.MyDirtyHack();
//return the instance
return service;
});
services.AddTransient<IOtherService, OtherService>(scope => {
//Explicitly ensuring the ctor function above is called, and also showcasing why this is an anti-pattern.
scope.GetService<IServiceINeedToUse>();
//TODO: Clean up both the IServiceINeedToUse and IOtherService configuration here, then somehow rebuild the service tree.
//Wow!
return new OtherService(somevalue);
});
}

修复这种模式的方法是让OtherService显式依赖于IServiceINeedToUse,而不是隐式依赖于它或它的方法的返回值…或者以其他方式显式地解决依赖关系。

您可以使用IApplicationBuilder实例以这种方式注入依赖项

public void Configure(IApplicationBuilder app)
{
//---------- Your code


using (var serviceScope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope())
{
var resultLogic = serviceScope.ServiceProvider.GetService<IResultLogic>();
resultLogic.YourMethod();
}


//---------- Your code
}
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<ISelfServiceConfigLoad, SelfServiceConfigLoader>();
var sp = services.BuildServiceProvider();
var configservice = sp.GetServices<ISelfServiceConfigLoad>();
services.AddSingleton<IExtractor, ConfigExtractor>( sp =>
{
var con = sp.GetRequiredService<ISelfServiceConfigLoad>();
var config = con.Load();
return new ConfigExtractor(config.Result);
});
services.AddSingleton<IProcessor<EventMessage>, SelfServiceProcessor>();
services.AddTransient<ISolrPush, SolrDataPush>();
services.AddSingleton<IAPICaller<string, string>, ApiRestCaller<string, string>>();
services.AddSingleton<IDataRetriever<SelfServiceApiRequest, IDictionary<string, object>>, SelfServiceDataRetriever>();
}