Resolver instancias con ASP.NET Core DI desde ConfigureServices

Resuelto Dave New asked hace 9 años • 9 respuestas

¿Cómo resuelvo manualmente un tipo utilizando el marco de inyección de dependencia integrado de ASP.NET Core MVC?

Configurar el contenedor es bastante fácil:

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

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

¿ Pero cómo puedo solucionarlo ISomeServicesin realizar la inyección? Por ejemplo, quiero hacer esto:

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

No existen tales métodos en IServiceCollection.

Dave New avatar Sep 08 '15 20:09 Dave New
Aceptado

La IServiceCollectioninterfaz se utiliza para construir un contenedor de inyección de dependencias. Una vez que esté completamente compilado, se compone en una IServiceProviderinstancia que puede usar para resolver servicios. Puedes inyectar un IServiceProvideren cualquier clase. Las clases IApplicationBuildery HttpContexttambién pueden proporcionar al proveedor de servicios, a través de sus propiedades ApplicationServiceso RequestServicesrespectivamente.

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

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

También hay varios métodos de extensión convenientes disponibles, como serviceProvider.GetService<IFooService>()(agregar un usingfor Microsoft.Extensions.DependencyInjection).

Resolver servicios dentro de la clase de inicio.

Inyectando dependencias

El proveedor de servicios de alojamiento del tiempo de ejecución puede inyectar ciertos servicios en el constructor de la Startupclase, como IConfiguration, IWebHostEnvironment( IHostingEnvironmenten versiones anteriores a 3.0) ILoggerFactoryy IServiceProvider. Tenga en cuenta que esta última es una instancia creada por la capa de alojamiento y contiene sólo los servicios esenciales para iniciar una aplicación .

El ConfigureServices()método no permite inyectar servicios, solo acepta un IServiceCollectionargumento. Esto tiene sentido porque ConfigureServices()es donde registras los servicios requeridos por tu aplicación. Sin embargo, puedes utilizar los servicios inyectados en 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
}

Cualquier servicio registrado ConfigureServices()se puede inyectar en el Configure()método; puede agregar una cantidad arbitraria de servicios después del IApplicationBuilderparámetro:

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

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

Resolver dependencias manualmente

Si necesita resolver servicios manualmente, preferiblemente debe utilizar lo ApplicationServicesproporcionado IApplicationBuilderen el Configure()método:

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

Es posible pasar y usar directamente an IServiceProvideren el constructor de su Startupclase, pero como se indicó anteriormente, esto contendrá un subconjunto limitado de servicios y, por lo tanto, tiene una utilidad limitada:

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

Si debe resolver servicios en el ConfigureServices()método, se requiere un enfoque diferente. Puede crear un intermediario IServiceProvidera partir de la IServiceCollectioninstancia que contiene los servicios que se han registrado hasta ese momento :

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

Tenga en cuenta: generalmente debe evitar resolver servicios dentro del ConfigureServices()método, ya que este es en realidad el lugar donde está configurando los servicios de la aplicación. A veces solo necesitas acceso a una IOptions<MyOptions>instancia. Puede lograr esto vinculando los valores de la IConfigurationinstancia a una instancia de MyOptions(que es esencialmente lo que hace el marco de opciones):

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

O use una sobrecarga para AddSingleton/AddScoped/AddTransient:

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

La resolución manual de servicios (también conocida como Localizador de servicios) generalmente se considera un antipatrón . Si bien tiene sus casos de uso (para marcos y/o capas de infraestructura), debes evitarlo tanto como sea posible.

Henk Mollema avatar Sep 08 '2015 15:09 Henk Mollema

La resolución manual de instancias implica el uso de la IServiceProviderinterfaz:

Resolver la dependencia en Startup.ConfigureServices

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

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

Resolver dependencias en Startup.Configure

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

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

    // ...
}

Resolver dependencias en Startup.Configure en ASP.NET Core 3

public void Configure(
    IApplicationBuilder application,
    IWebHostEnvironment webHostEnvironment)
{
    application.ApplicationServices.GetService<MyService>();
}

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

Resolución de dependencias en acciones del controlador

[HttpGet("/some-action")]
public string SomeAction([FromServices] IMyService myService) => "Hello";
Muhammad Rehan Saeed avatar Sep 08 '2015 14:09 Muhammad Rehan Saeed

Si generas una aplicación con una plantilla tendrás algo como esto en la Startupclase:

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

    services.AddMvc();
}

Luego puedes agregar dependencias allí, por ejemplo:

services.AddTransient<ITestService, TestService>();

Si desea acceder ITestServicea su controlador, puede agregar IServiceProviderel constructor y se inyectará:

public HomeController(IServiceProvider serviceProvider)

Luego podrás resolver el servicio que agregaste:

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

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

using Microsoft.Extensions.DependencyInjection;

ITestService.cs

public interface ITestService
{
    int GenerateRandom();
}

TestService.cs

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

Startup.cs (Configurar servicios)

public void ConfigureServices(IServiceCollection services)
{
    services.AddApplicationInsightsTelemetry(Configuration);
    services.AddMvc();

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

InicioController.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();
        }
BrunoLM avatar Jul 06 '2016 22:07 BrunoLM