c# - mvc - tag helper asp net core 2
ResoluciĆ³n de instancias con ASP.NET Core DI (6)
La interfaz
IServiceCollection
se usa para
construir
un contenedor de inyección de dependencia.
Una vez que está completamente construido, se compone de una instancia
IServiceProvider
que puede usar para resolver servicios.
Puede inyectar un
IServiceProvider
en cualquier clase.
Las clases
IApplicationBuilder
y
HttpContext
pueden proporcionar el proveedor de servicios, a través de las propiedades
ApplicationServices
o
RequestServices
respectivamente.
IServiceProvider
define un
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 disponibles, como
serviceProvider.GetService<IFooService>()
(agregue un
using
para
Microsoft.Extensions.DependencyInjection
).
Resolviendo servicios dentro de la clase de inicio
Inyectando dependencias
El tiempo de ejecución puede inyectar servicios en el constructor de la clase
Startup
, como
IHostingEnvironment
,
IConfiguration
e
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 una lista arbitraria 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 de
aplicaciones
en lugar del proveedor de servicios de
alojamiento
.
public void Configure(IApplicationBuilder app, IFooService fooService)
{
// ...
}
Sin embargo, el método
ConfigureServices()
no permite inyectar servicios, solo acepta un argumento
IServiceCollection
.
Este es el método donde configura el contenedor de inyección de dependencia de la aplicación.
Puede usar 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
}
Resolver dependencias manualmente
Si desea resolver servicios manualmente, puede dejar que el tiempo de ejecución inyecte una instancia de
IServiceProvider
en el constructor o use los
ApplicationServices
proporcionados 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
IServiceProvider
de una instancia de
IServiceCollection
que contenga los 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>();
}
Necesita el paquete
Microsoft.Extensions.DependencyInjection
para esto.
Tenga en cuenta:
En general, no debe resolver los servicios dentro del método
ConfigureServices()
, ya que este es realmente el lugar donde está
configurando
los servicios de la aplicación.
A veces solo necesita acceder a alguna
IOptions<MyOptions>
.
Puede lograr esto vinculando los valores de la instancia de
IConfiguration
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);
}
La resolución manual de servicios (también conocido como Localizador de servicios) en general se conoce como antipatrón . Si bien tiene sus casos de uso (para marcos y / o capas de infraestructura), debe evitarlo tanto como sea posible.
¿Cómo resuelvo manualmente un tipo usando el marco de inyección de dependencia incorporado ASP.NET Core MVC?
Configurar el contenedor es bastante fácil:
public void ConfigureServices(IServiceCollection services)
{
// ...
services.AddTransient<ISomeService, SomeConcreteService>();
}
Pero, ¿cómo puedo resolver
ISomeService
sin realizar una inyección?
Por ejemplo, quiero hacer esto:
ISomeService service = services.Resolve<ISomeService>();
No existen tales métodos en
IServiceCollection
.
La resolución manual de instancias implica el uso de la interfaz
IServiceProvider
:
Resolviendo 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 el inicio.
public void Configure(
IApplicationBuilder application,
IServiceProvider serviceProvider)
{
// By type.
var service1 = (MyService)serviceProvider.GetService(typeof(MyService));
// Using extension method.
var service2 = serviceProvider.GetService<MyService>();
// ...
}
Uso de servicios inyectados en tiempo de ejecución
Algunos tipos se pueden inyectar como parámetros de 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)
{
}
}
Resolver dependencias en acciones del controlador
[HttpGet("/some-action")]
public string SomeAction([FromServices] IMyService myService) => "Hello";
Puede inyectar dependencias en atributos como AuthorizeAttribute de esta manera
var someservice = (ISomeService)context.HttpContext.RequestServices.GetService(typeof(ISomeService));
Si genera una aplicación con una plantilla, tendrá algo como esto en la clase de
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 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 (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();
}
Si solo necesita resolver una dependencia con el fin de pasarla al constructor de otra dependencia que está registrando, puede hacerlo.
Digamos que tenía un servicio que incluía una cadena y un ISomeService.
public class AnotherService : IAnotherService
{
public AnotherService(ISomeService someService, string serviceUrl)
{
...
}
}
Cuando vaya a registrar esto dentro de Startup.cs, deberá hacer esto:
services.AddScoped<IAnotherService>(ctx =>
new AnotherService(ctx.GetService<ISomeService>(), "https://someservice.com/")
);
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddDbContext<ConfigurationRepository>(options =>
options.UseSqlServer(Configuration.GetConnectionString("SqlConnectionString")));
services.AddScoped<IConfigurationBL, ConfigurationBL>();
services.AddScoped<IConfigurationRepository, ConfigurationRepository>();
}