Resolución de instancias con ASP.NET Core DI


¿Cómo resuelvo manualmente un tipo usando el ASP.NET Core MVC built-in dependency injection framework?

Configurar el contenedor es bastante fácil:

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

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

Pero, ¿cómo puedo resolver ISomeService sin realizar la inyección? Por ejemplo, quiero hacer esto:

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

No hay tales métodos en IServiceCollection.

Author: Henk Mollema, 2015-09-08

4 answers

La interfaz IServiceCollection se utiliza para construir un contenedor de inyección de dependencias. Después de que se compila completamente, se compone a una instancia IServiceProvider que puede usar para resolver servicios. Puede inyectar un IServiceProvider en cualquier clase. Las clases IApplicationBuilder y HttpContext también pueden proporcionar el proveedor de servicios, a través de las propiedades ApplicationServices o RequestServices respectivamente.

IServiceProvider define un método GetService(Type type) para resolver un servicio:

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

También hay varios métodos de extensión de conveniencia disponible, como serviceProvider.GetService<IFooService>(). (Añádase un uso para Microsoft.Extensions.DependencyInjection).

Resolviendo servicios dentro de la clase startup

Inyectando dependencias

El tiempo de ejecución puede inyectar servicios en el constructor de la clase Startup, tales como IHostingEnvironment, IConfiguration y IServiceProvider. Tenga en cuenta que este proveedor de servicios es una instancia creada por la capa de alojamiento y contiene solo los servicios para iniciar una aplicación.

Los servicios también se pueden inyectar en el método Configure(). Puede agregar un arbitrario cantidad de parámetros después del parámetro IApplicationBuilder. También puede inyectar sus propios servicios que están registrados en el método ConfigureServices() aquí, se resolverán desde el proveedor de servicios aplicación en lugar del proveedor de servicios alojamiento.

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

El método ConfigureServices() sin embargo, no permite servicios de inyección, solo acepta un argumento IServiceCollection. Este es el método en el que se configura el contenedor de inyección de dependencias de la aplicación. Puede utilizar los servicios inyectados el constructor de la startup aquí. Por ejemplo:

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

public IConfiguration Configuration { get; }

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

Resolviendo manualmente dependencias

Si desea resolver manualmente los servicios, puede dejar que el tiempo de ejecución inyecte una instancia IServiceProvider en el constructor o usar el ApplicationServices proporcionado por IApplicationBuilder en el método Configure():

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

O

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

Sin embargo, si necesita resolver servicios en el método ConfigureServices(), necesita un enfoque diferente. Puede crear un IServiceProvider intermedio a partir de una instancia IServiceCollection que contenga el servicios que están registrados hasta entonces:

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<IFooService, FooService>();

    // Build the intermediate service provider
    var sp = services.BuildServiceProvider();
    var fooService = sp.GetService<IFooService>();
}

Usted necesita el Microsoft.Extensions.DependencyInjection paquete para esto.

Tenga en cuenta:
Generalmente no debe resolver servicios dentro del método ConfigureServices(), ya que este es en realidad el lugar donde está configurando los servicios de la aplicación. A veces solo necesita acceso a alguna instancia IOptions<MyOptions>. Puede lograr esto vinculando los valores de la instancia IConfiguration a una instancia de MyOptions (que es esencialmente lo que el options framework does):

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

Resolver manualmente los servicios (también conocido como Localizador de servicios) en general es conocido como un anti-patrón. Si bien tiene sus casos de uso (para marcos y/o capas de infraestructura), debe evitarlo tanto como sea posible.

 205
Author: Henk Mollema,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-12 12:01:53

La resolución manual de instancias implica el uso de IServiceProvider interfaz:

Resolviendo la dependencia en el inicio.ConfigureServices

public void ConfigureServices(IServiceCollection services)
{
    services.AddTransient<IMyService, MyService>();

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

Resolviendo dependencias en el Inicio.Configurar

public void Configure(
    IApplicationBuilder application,
    IServiceProvider serviceProvider)
{
    // By type.
    var service1 = (MyService)serviceProvider.GetService(typeof(MyService));

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

    // ...
}

Usando Servicios Inyectados en Tiempo de Ejecución

Algunos tipos se pueden inyectar como parámetros del método:

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)
    {
    }
}

Resolviendo Dependencias en Acciones del Controlador

[HttpGet("/some-action")]
public string SomeAction([FromServices] IMyService myService) => "Hello";
 46
Author: Muhammad Rehan Saeed,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-31 07:21:29

Si generas una aplicación con una plantilla vas a tener algo como esto en la clase Startup:

public void ConfigureServices(IServiceCollection services)
{
    // Add framework services.
    services.AddApplicationInsightsTelemetry(Configuration);

    services.AddMvc();
}

Luego puede agregar dependencias allí, por ejemplo:

services.AddTransient<ITestService, TestService>();

Si desea acceder a ITestService en su controlador puede agregar IServiceProvider en el constructor y se inyectará:

public HomeController(IServiceProvider serviceProvider)

Luego puede resolver el servicio que agregó:

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

Tenga en cuenta que para usar la versión genérica debe incluir el espacio de nombres con el extensiones:

using Microsoft.Extensions.DependencyInjection;

ITestService.cs

public interface ITestService
{
    int GenerateRandom();
}

TestService.cs

public class TestService : ITestService
{
    public int GenerateRandom()
    {
        return 4;
    }
}

Inicio.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();
        }
 13
Author: BrunoLM,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-07-06 22:59:56
public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();

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

    services.AddScoped<IConfigurationBL, ConfigurationBL>();
    services.AddScoped<IConfigurationRepository, ConfigurationRepository>();
}
 -3
Author: Nathan Alard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-02-14 21:55:15