Inserción de dependencias en ASP.NET CoreDependency injection in ASP.NET Core

Por Kirk Larkin, Steve Smith, Scott Addie y Brandon DahlerBy Kirk Larkin, Steve Smith, Scott Addie, and Brandon Dahler

ASP.NET Core admite el patrón de diseño de software de inserción de dependencias (DI), que es una técnica para conseguir la inversión de control (IoC) entre clases y sus dependencias.ASP.NET Core supports the dependency injection (DI) software design pattern, which is a technique for achieving Inversion of Control (IoC) between classes and their dependencies.

Para más información específica sobre la inserción de dependencias en los controladores MVC, vea Inserción de dependencias en controladores en ASP.NET Core.For more information specific to dependency injection within MVC controllers, see Inserción de dependencias en controladores en ASP.NET Core.

Para obtener más información sobre la inserción de dependencias de las opciones, vea Patrón de opciones en ASP.NET Core.For more information on dependency injection of options, see Patrón de opciones en ASP.NET Core.

En este tema se proporciona información sobre la inyección de dependencias en ASP.NET Core.This topic provides information on dependency injection in ASP.NET Core. Para obtener información sobre el uso de la inyección de dependencias en las aplicaciones de consola, consulte Inyección de dependencias en .NET.For information on using dependency injection in console apps, see Dependency injection in .NET.

Vea o descargue el código de ejemplo (cómo descargarlo)View or download sample code (how to download)

Información general sobre la inserción de dependenciasOverview of dependency injection

Una dependencia es un objeto del que depende otro objeto.A dependency is an object that another object depends on. Examine la clase MyDependency siguiente con un método WriteMessage del que dependen otras clases:Examine the following MyDependency class with a WriteMessage method that other classes depend on:

public class MyDependency
{
    public void WriteMessage(string message)
    {
        Console.WriteLine($"MyDependency.WriteMessage called. Message: {message}");
    }
}

Una clase puede crear una instancia de la clase MyDependency para usar su método WriteMessage.A class can create an instance of the MyDependency class to make use of its WriteMessage method. En el ejemplo siguiente, la clase MyDependency es una dependencia de la clase IndexModel:In the following example, the MyDependency class is a dependency of the IndexModel class:

public class IndexModel : PageModel
{
    private readonly MyDependency _dependency = new MyDependency();

    public void OnGet()
    {
        _dependency.WriteMessage("IndexModel.OnGet created this message.");
    }
}

La clase crea y depende directamente de la clase MyDependency.The class creates and directly depends on the MyDependency class. Las dependencias de código, como en el ejemplo anterior, son problemáticas y deben evitarse por las razones siguientes:Code dependencies, such as in the previous example, are problematic and should be avoided for the following reasons:

  • Para reemplazar MyDependency por una implementación diferente, se debe modificar la clase IndexModel.To replace MyDependency with a different implementation, the IndexModel class must be modified.
  • Si MyDependency tiene dependencias, deben configurarse según la clase IndexModel.If MyDependency has dependencies, they must also be configured by the IndexModel class. En un proyecto grande con varias clases que dependen de MyDependency, el código de configuración se dispersa por la aplicación.In a large project with multiple classes depending on MyDependency, the configuration code becomes scattered across the app.
  • Esta implementación es difícil para realizar pruebas unitarias.This implementation is difficult to unit test. La aplicación debe usar una clase MyDependency como boceto o código auxiliar, que no es posible con este enfoque.The app should use a mock or stub MyDependency class, which isn't possible with this approach.

La inserción de dependencias aborda estos problemas mediante:Dependency injection addresses these problems through:

  • Uso de una interfaz o clase base para abstraer la implementación de dependencias.The use of an interface or base class to abstract the dependency implementation.
  • Registro de la dependencia en un contenedor de servicios.Registration of the dependency in a service container. ASP.NET Core proporciona un contenedor de servicios integrado, IServiceProvider.ASP.NET Core provides a built-in service container, IServiceProvider. Por lo general, los servicios se registran en el método Startup.ConfigureServices de la aplicación.Services are typically registered in the app's Startup.ConfigureServices method.
  • Inserción del servicio en el constructor de la clase en la que se usa.Injection of the service into the constructor of the class where it's used. El marco de trabajo asume la responsabilidad de crear una instancia de la dependencia y de desecharla cuando ya no es necesaria.The framework takes on the responsibility of creating an instance of the dependency and disposing of it when it's no longer needed.

En la aplicación de ejemplo, la interfaz IMyDependency define el método WriteMessage:In the sample app, the IMyDependency interface defines the WriteMessage method:

public interface IMyDependency
{
    void WriteMessage(string message);
}

Esta interfaz se implementa mediante un tipo concreto, MyDependency:This interface is implemented by a concrete type, MyDependency:

public class MyDependency : IMyDependency
{
    public void WriteMessage(string message)
    {
        Console.WriteLine($"MyDependency.WriteMessage Message: {message}");
    }
}

La aplicación de ejemplo registra el servicio IMyDependency con el tipo concreto MyDependency.The sample app registers the IMyDependency service with the concrete type MyDependency. El método AddScoped registra el servicio mediante una duración con ámbito, definida como la duración de una única solicitud.The AddScoped method registers the service with a scoped lifetime, the lifetime of a single request. Las duraciones del servicio se describen más adelante en este tema.Service lifetimes are described later in this topic.

public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped<IMyDependency, MyDependency>();

    services.AddRazorPages();
}

En la aplicación de ejemplo, el servicio IMyDependency se solicita y usa para llamar al método WriteMessage:In the sample app, the IMyDependency service is requested and used to call the WriteMessage method:

public class Index2Model : PageModel
{
    private readonly IMyDependency _myDependency;

    public Index2Model(IMyDependency myDependency)
    {
        _myDependency = myDependency;            
    }

    public void OnGet()
    {
        _myDependency.WriteMessage("Index2Model.OnGet");
    }
}

Mediante el uso del patrón de DI, el controlador:By using the DI pattern, the controller:

  • No usa el tipo concreto MyDependency, solo la interfaz IMyDependency que implementa.Doesn't use the concrete type MyDependency, only the IMyDependency interface it implements. Esto facilita el cambio de la implementación que el controlador utiliza sin modificar el controlador.That makes it easy to change the implementation that the controller uses without modifying the controller.
  • No crea una instancia de MyDependency, la crea el contenedor de DI.Doesn't create an instance of MyDependency, it's created by the DI container.

La implementación de la interfaz de IMyDependency se puede mejorar mediante el uso de la API de registro integrada:The implementation of the IMyDependency interface can be improved by using the built-in logging API:

public class MyDependency2 : IMyDependency
{
    private readonly ILogger<MyDependency2> _logger;

    public MyDependency2(ILogger<MyDependency2> logger)
    {
        _logger = logger;
    }

    public void WriteMessage(string message)
    {
        _logger.LogInformation( $"MyDependency2.WriteMessage Message: {message}");
    }
}

El método ConfigureServices actualizado registra la nueva implementación de IMyDependency:The updated ConfigureServices method registers the new IMyDependency implementation:

public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped<IMyDependency, MyDependency2>();

    services.AddRazorPages();
}

MyDependency2 depende de ILogger<TCategoryName>, el que solicita en el constructor.MyDependency2 depends on ILogger<TCategoryName>, which it requests in the constructor. ILogger<TCategoryName> es un servicio proporcionado por el marco de trabajo.ILogger<TCategoryName> is a framework-provided service.

No es raro usar la inserción de dependencias de forma encadenada.It's not unusual to use dependency injection in a chained fashion. Cada dependencia solicitada a su vez solicita sus propias dependencias.Each requested dependency in turn requests its own dependencies. El contenedor resuelve las dependencias del gráfico y devuelve el servicio totalmente resuelto.The container resolves the dependencies in the graph and returns the fully resolved service. El conjunto colectivo de dependencias que deben resolverse suele denominarse árbol de dependencias, gráfico de dependencias o gráfico de objetos.The collective set of dependencies that must be resolved is typically referred to as a dependency tree, dependency graph, or object graph.

El contenedor resuelve ILogger<TCategoryName> aprovechando las ventajas de los tipos abiertos (genéricos), lo que elimina la necesidad de registrar todos los tipos construidos (genéricos).The container resolves ILogger<TCategoryName> by taking advantage of (generic) open types, eliminating the need to register every (generic) constructed type.

En la terminología de la inserción de dependencias, un servicio:In dependency injection terminology, a service:

  • Por lo general, es un objeto que proporciona un servicio a otros objetos, como el servicio IMyDependency.Is typically an object that provides a service to other objects, such as the IMyDependency service.
  • No está relacionado con un servicio web, aunque el servicio puede utilizar un servicio web.Is not related to a web service, although the service may use a web service.

El marco de trabajo proporciona un sistema de registro sólido.The framework provides a robust logging system. Las implementaciones de IMyDependency que se muestran en los ejemplos anteriores se escribieron para mostrar la inserción de DI básica, no para implementar el registro.The IMyDependency implementations shown in the preceding examples were written to demonstrate basic DI, not to implement logging. La mayoría de las aplicaciones no deberían tener que escribir registradores.Most apps shouldn't need to write loggers. En el código siguiente se muestra cómo usar el registro predeterminado, que no requiere que los servicios se registren en ConfigureServices:The following code demonstrates using the default logging, which doesn't require any services to be registered in ConfigureServices:

public class AboutModel : PageModel
{
    private readonly ILogger _logger;

    public AboutModel(ILogger<AboutModel> logger)
    {
        _logger = logger;
    }
    
    public string Message { get; set; }

    public void OnGet()
    {
        Message = $"About page visited at {DateTime.UtcNow.ToLongTimeString()}";
        _logger.LogInformation(Message);
    }
}

Con el código anterior, no es necesario actualizar ConfigureServices porque el registro se proporciona a través del marco de trabajo.Using the preceding code, there is no need to update ConfigureServices, because logging is provided by the framework.

Servicios insertados en StartupServices injected into Startup

Los servicios se pueden insertar en el constructor Startup y en el método Startup.Configure.Services can be injected into the Startup constructor and the Startup.Configure method.

Solo se pueden insertar los servicios siguientes en el constructor Startup cuando se usa el host genérico (IHostBuilder):Only the following services can be injected into the Startup constructor when using the Generic Host (IHostBuilder):

Cualquier servicio registrado con el contenedor de DI se puede insertar en el método Startup.Configure:Any service registered with the DI container can be injected into the Startup.Configure method:

public void Configure(IApplicationBuilder app, ILogger<Startup> logger)
{
    ...
}

Para obtener más información, vea Inicio de la aplicación en ASP.NET Core y Acceso a la configuración en Inicio.For more information, see Inicio de la aplicación en ASP.NET Core and Access configuration in Startup.

Registro de grupos de servicios con métodos de extensiónRegister groups of services with extension methods

El marco ASP.NET Core usa una convención para registrar un grupo de servicios relacionados.The ASP.NET Core framework uses a convention for registering a group of related services. La convención es usar un único método de extensión de Add{GROUP_NAME} para registrar todos los servicios requeridos por una característica de marco.The convention is to use a single Add{GROUP_NAME} extension method to register all of the services required by a framework feature. Por ejemplo, el método de extensión <Microsoft.Extensions.DependencyInjection.MvcServiceCollectionExtensions.AddControllers> registra los servicios necesarios para los controladores MVC.For example, the <Microsoft.Extensions.DependencyInjection.MvcServiceCollectionExtensions.AddControllers> extension method registers the services required for MVC controllers.

El código siguiente lo genera la plantilla de Razor Pages con cuentas de usuario individuales y muestra cómo agregar servicios adicionales al contenedor mediante los métodos de extensión AddDbContext y AddDefaultIdentity:The following code is generated by the Razor Pages template using individual user accounts and shows how to add additional services to the container using the extension methods AddDbContext and AddDefaultIdentity:

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));
    services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
        .AddEntityFrameworkStores<ApplicationDbContext>();
    services.AddRazorPages();
}

Tenga en cuenta el siguiente método ConfigureServices, que registra los servicios y configura las opciones:Consider the following ConfigureServices method, which registers services and configures options:

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<PositionOptions>(
        Configuration.GetSection(PositionOptions.Position));
    services.Configure<ColorOptions>(
        Configuration.GetSection(ColorOptions.Color));

    services.AddScoped<IMyDependency, MyDependency>();
    services.AddScoped<IMyDependency2, MyDependency2>();

    services.AddRazorPages();
}

Los grupos de registros relacionados pueden moverse a un método de extensión para registrar los servicios.Related groups of registrations can be moved to an extension method to register services. Por ejemplo, los servicios de configuración se agregan a la siguiente clase:For example, the configuration services are added to the following class:

using ConfigSample.Options;
using Microsoft.Extensions.Configuration;

namespace Microsoft.Extensions.DependencyInjection
{
    public static class MyConfigServiceCollectionExtensions
    {
        public static IServiceCollection AddConfig(
             this IServiceCollection services, IConfiguration config)
        {
            services.Configure<PositionOptions>(
                config.GetSection(PositionOptions.Position));
            services.Configure<ColorOptions>(
                config.GetSection(ColorOptions.Color));

            return services;
        }
    }
}

Los servicios restantes se registran en una clase similar.The remaining services are registered in a similar class. El siguiente método ConfigureServices usa los nuevos métodos de extensión para registrar los servicios:The following ConfigureServices method uses the new extension methods to register the services:

public void ConfigureServices(IServiceCollection services)
{
    services.AddConfig(Configuration)
            .AddMyDependencyGroup();

    services.AddRazorPages();
}

Nota: Cada método de extensión services.Add{GROUP_NAME} agrega servicios y potencialmente los configura.Note: Each services.Add{GROUP_NAME} extension method adds and potentially configures services. Por ejemplo, AddControllersWithViews agrega los controladores MVC de servicios con vistas que se requieren y AddRazorPages agrega los servicios que Razor Pages requiere.For example, AddControllersWithViews adds the services MVC controllers with views require, and AddRazorPages adds the services Razor Pages requires. Se recomienda que las aplicaciones sigan esta convención de nomenclatura.We recommended that apps follow this naming convention. Coloque los métodos de extensión en el espacio de nombres Microsoft.Extensions.DependencyInjection para encapsular grupos de registros del servicio.Place extension methods in the Microsoft.Extensions.DependencyInjection namespace to encapsulate groups of service registrations.

Duraciones de serviciosService lifetimes

Los servicios se pueden registrar con una de las duraciones siguientes:Services can be registered with one of the following lifetimes:

  • TransitorioTransient
  • Con ámbitoScoped
  • SingletonSingleton

En las secciones siguientes se describen cada una de las duraciones anteriores.The following sections describe each of the preceding lifetimes. Elija una duración adecuada para cada servicio registrado.Choose an appropriate lifetime for each registered service.

TransitorioTransient

Los servicios de duración transitoria se crean cada vez que el contenedor del servicio los solicita.Transient lifetime services are created each time they're requested from the service container. Esta duración funciona mejor para servicios sin estado ligeros.This lifetime works best for lightweight, stateless services. Registre los servicios transitorios con AddTransient.Register transient services with AddTransient.

En las aplicaciones que procesan solicitudes, los servicios transitorios se eliminan al final de la solicitud.In apps that process requests, transient services are disposed at the end of the request.

Con ámbitoScoped

Los servicios de duración con ámbito se crean una vez por solicitud del cliente (conexión).Scoped lifetime services are created once per client request (connection). Registre los servicios con ámbito con AddScoped.Register scoped services with AddScoped.

En las aplicaciones que procesan solicitudes, los servicios con ámbito se eliminan al final de la solicitud.In apps that process requests, scoped services are disposed at the end of the request.

Cuando se usa Entity Framework Core, el método de extensión AddDbContext registra tipos de DbContext con una duración de ámbito de forma predeterminada.When using Entity Framework Core, the AddDbContext extension method registers DbContext types with a scoped lifetime by default.

Nunca resuelva un servicio con ámbito desde un singleton.Do not resolve a scoped service from a singleton. Puede dar lugar a que el servicio adopte un estado incorrecto al procesar solicitudes posteriores.It may cause the service to have incorrect state when processing subsequent requests. Basta con:It's fine to:

  • Resolver un servicio singleton desde un servicio con ámbito o transitorio.Resolve a singleton service from a scoped or transient service.
  • Resolver un servicio con ámbito desde otro servicio con ámbito o transitorio.Resolve a scoped service from another scoped or transient service.

De manera predeterminada, en el entorno de desarrollo, resolver un servicio desde otro servicio con una duración más larga genera una excepción.By default, in the development environment, resolving a service from another service with a longer lifetime throws an exception. Para más información, vea Validación del ámbito.For more information, see Scope validation.

Para usar servicios con ámbito en middleware, use uno de los enfoques siguientes:To use scoped services in middleware, use one of the following approaches:

  • Inserte el servicio en el método Invoke o InvokeAsync del middleware.Inject the service into the middleware's Invoke or InvokeAsync method. El uso de la inserción de constructores genera una excepción en tiempo de ejecución porque obliga al servicio con ámbito a comportarse como un singleton.Using constructor injection throws a runtime exception because it forces the scoped service to behave like a singleton. El ejemplo que aparece en la sección Opciones de registro y duración muestra el enfoque InvokeAsync.The sample in the Lifetime and registration options section demonstrates the InvokeAsync approach.
  • Use middleware basado en Factory.Use Factory-based middleware. El middleware registrado mediante este enfoque se activa por solicitud de cliente (conexión), lo que permite que los servicios con ámbito se inserten en el método InvokeAsync del middleware.Middleware registered using this approach is activated per client request (connection), which allows scoped services to be injected into the middleware's InvokeAsync method.

Para obtener más información, vea Escritura de middleware de ASP.NET Core personalizado.For more information, see Escritura de middleware de ASP.NET Core personalizado.

SingletonSingleton

Los servicios de duración de singleton se crean de alguna de las formas siguientes:Singleton lifetime services are created either:

  • La primera vez que se solicitan.The first time they're requested.
  • Mediante el desarrollador, al proporcionar una instancia de implementación directamente al contenedor.By the developer, when providing an implementation instance directly to the container. Este enfoque rara vez es necesario.This approach is rarely needed.

Cada solicitud posterior usa la misma instancia.Every subsequent request uses the same instance. Si la aplicación requiere un comportamiento de singleton, permita que el contenedor de servicios administre la duración del servicio.If the app requires singleton behavior, allow the service container to manage the service's lifetime. No implemente el modelo de diseño singleton y proporcione el código para desechar el singleton.Don't implement the singleton design pattern and provide code to dispose of the singleton. Los servicios nunca deben desecharse mediante el código que haya resuelto el servicio del contenedor.Services should never be disposed by code that resolved the service from the container. Si un tipo o fábrica se registra como singleton, el contenedor elimina el singleton de manera automática.If a type or factory is registered as a singleton, the container disposes the singleton automatically.

Registre los servicios singleton con AddSingleton.Register singleton services with AddSingleton. Los servicios singleton deben ser seguros para los subprocesos y se suelen usar en servicios sin estado.Singleton services must be thread safe and are often used in stateless services.

En las aplicaciones que procesan solicitudes, los servicios singleton se eliminan cuando ServiceProvider se elimina al cerrarse la aplicación.In apps that process requests, singleton services are disposed when the ServiceProvider is disposed on application shutdown. Como no se libera memoria hasta que se apaga la aplicación, se debe tener en cuenta el uso de memoria con un servicio singleton.Because memory is not released until the app is shut down, consider memory use with a singleton service.

Advertencia

Nunca resuelva un servicio con ámbito desde un singleton.Do not resolve a scoped service from a singleton. Puede dar lugar a que el servicio adopte un estado incorrecto al procesar solicitudes posteriores.It may cause the service to have incorrect state when processing subsequent requests. Basta con resolver un servicio singleton desde un servicio con ámbito o transitorio.It's fine to resolve a singleton service from a scoped or transient service.

Métodos de registro del servicioService registration methods

El marco proporciona métodos de extensión de registro del servicio que resultan útiles en escenarios específicos:The framework provides service registration extension methods that are useful in specific scenarios:

MétodoMethod AutomáticoAutomatic
objetoobject
eliminacióndisposal
MúltipleMultiple
implementacionesimplementations
Transferencia de argumentosPass args
Add{LIFETIME}<{SERVICE}, {IMPLEMENTATION}>()
Ejemplo:Example:
services.AddSingleton<IMyDep, MyDep>();
Yes Yes NoNo
Add{LIFETIME}<{SERVICE}>(sp => new {IMPLEMENTATION})
Ejemplos:Examples:
services.AddSingleton<IMyDep>(sp => new MyDep());
services.AddSingleton<IMyDep>(sp => new MyDep(99));
Yes Yes Yes
Add{LIFETIME}<{IMPLEMENTATION}>()
Ejemplo:Example:
services.AddSingleton<MyDep>();
Yes NoNo NoNo
AddSingleton<{SERVICE}>(new {IMPLEMENTATION})
Ejemplos:Examples:
services.AddSingleton<IMyDep>(new MyDep());
services.AddSingleton<IMyDep>(new MyDep(99));
NoNo Yes Yes
AddSingleton(new {IMPLEMENTATION})
Ejemplos:Examples:
services.AddSingleton(new MyDep());
services.AddSingleton(new MyDep(99));
NoNo NoNo Yes

Para obtener más información sobre el tipo de eliminación, consulte la sección Eliminación de servicios.For more information on type disposal, see the Disposal of services section. Es habitual usar varias implementaciones al utilizar tipos de simulación para las pruebas.It's common to use multiple implementations when mocking types for testing.

El marco también proporciona métodos de extensión TryAdd{LIFETIME}, que registran el servicio solo si todavía no hay registrada una implementación.The framework also provides TryAdd{LIFETIME} extension methods, which register the service only if there isn't already an implementation registered.

En el ejemplo siguiente, la llamada a AddSingleton registra MyDependency como una implementación para IMyDependency.In the following example, the call to AddSingleton registers MyDependency as an implementation for IMyDependency. La llamada a TryAddSingleton no tiene ningún efecto porque IMyDependency ya tiene una implementación registrada:The call to TryAddSingleton has no effect because IMyDependency already has a registered implementation:

services.AddSingleton<IMyDependency, MyDependency>();
// The following line has no effect:
services.TryAddSingleton<IMyDependency, DifferentDependency>();

Para más información, consulte:For more information, see:

Los métodos TryAddEnumerable(ServiceDescriptor) registran el servicio solo si todavía no hay una implementación del mismo tipo.The TryAddEnumerable(ServiceDescriptor) methods register the service only if there isn't already an implementation of the same type. A través de IEnumerable<{SERVICE}> se resuelven varios servicios.Multiple services are resolved via IEnumerable<{SERVICE}>. Al registrar los servicios, el desarrollador debería agregar una instancia si no se ha agregado ya una del mismo tipo.When registering services, the developer should add an instance if one of the same type hasn't already been added. Por lo general, los autores de bibliotecas usan TryAddEnumerable para evitar el registro de varias copias de una implementación en el contenedor.Generally, library authors use TryAddEnumerable to avoid registering multiple copies of an implementation in the container.

En el ejemplo siguiente, la primera llamada a TryAddEnumerable registra MyDependency como una implementación para IMyDependency1.In the following example, the first call to TryAddEnumerable registers MyDependency as an implementation for IMyDependency1. La segunda llamada registra MyDependency para IMyDependency2.The second call registers MyDependency for IMyDependency2. La tercera llamada no tiene ningún efecto porque IMyDependency1 ya tiene una implementación registrada de MyDependency:The third call has no effect because IMyDependency1 already has a registered implementation of MyDependency:

public interface IMyDependency1 { }
public interface IMyDependency2 { }

public class MyDependency : IMyDependency1, IMyDependency2 { }

services.TryAddEnumerable(ServiceDescriptor.Singleton<IMyDependency1, MyDependency>());
services.TryAddEnumerable(ServiceDescriptor.Singleton<IMyDependency2, MyDependency>());
services.TryAddEnumerable(ServiceDescriptor.Singleton<IMyDependency1, MyDependency>());

Normalmente, el registro del servicio es independiente, excepto cuando se registran varias implementaciones del mismo tipo.Service registration is generally order independent except when registering multiple implementations of the same type.

IServiceCollection es una colección de objetos ServiceDescriptor.IServiceCollection is a collection of ServiceDescriptor objects. En el ejemplo siguiente se muestra cómo registrar un servicio mediante la creación e incorporación de un elemento ServiceDescriptor:The following example shows how to register a service by creating and adding a ServiceDescriptor:

var myKey = Configuration["MyKey"];
var descriptor = new ServiceDescriptor(
    typeof(IMyDependency),
    sp => new MyDependency5(myKey),
    ServiceLifetime.Transient);

services.Add(descriptor);

Los métodos Add{LIFETIME} integrados usan el mismo enfoque.The built-in Add{LIFETIME} methods use the same approach. Por ejemplo, consulte el código fuente de AddScoped.For example, see the AddScoped source code.

Comportamiento de inserción de constructorConstructor injection behavior

Los servicios se pueden resolver mediante el uso de:Services can be resolved by using:

Los constructores pueden aceptar argumentos que no se proporcionan mediante la inserción de dependencias, pero los argumentos deben asignar valores predeterminados.Constructors can accept arguments that aren't provided by dependency injection, but the arguments must assign default values.

Cuando se resuelven los servicios mediante IServiceProvider o ActivatorUtilities, la inserción del constructor requiere un constructor público.When services are resolved by IServiceProvider or ActivatorUtilities, constructor injection requires a public constructor.

Cuando se resuelven los servicios mediante ActivatorUtilities, la inserción del constructor requiere que exista solo un constructor aplicable.When services are resolved by ActivatorUtilities, constructor injection requires that only one applicable constructor exists. Se admiten las sobrecargas de constructor, pero solo puede existir una sobrecarga cuyos argumentos pueda cumplir la inserción de dependencias.Constructor overloads are supported, but only one overload can exist whose arguments can all be fulfilled by dependency injection.

Contextos de Entity FrameworkEntity Framework contexts

De manera predeterminada, los contextos de Entity Framework se agregan al contenedor de servicios mediante la duración con ámbito porque las operaciones de base de datos de aplicación web se suelen limitar a la solicitud de cliente.By default, Entity Framework contexts are added to the service container using the scoped lifetime because web app database operations are normally scoped to the client request. Para usar una duración distinta, especifíquela mediante el uso de una sobrecarga de AddDbContext.To use a different lifetime, specify the lifetime by using an AddDbContext overload. En los servicios de una duración determinada no se debe usar un contexto de base de datos con una duración más corta que la del servicio.Services of a given lifetime shouldn't use a database context with a lifetime that's shorter than the service's lifetime.

Opciones de registro y duraciónLifetime and registration options

Para mostrar la diferencia entre las duraciones del servicio y sus opciones de registro, considere las interfaces siguientes que representan una tarea como una operación con un identificador, OperationId.To demonstrate the difference between service lifetimes and their registration options, consider the following interfaces that represent a task as an operation with an identifier, OperationId. Según cómo esté configurada la duración de un servicio de operaciones para las interfaces siguientes, el contenedor proporciona las mismas instancias del servicio u otras distintas cuando así lo solicita la clase:Depending on how the lifetime of an operation's service is configured for the following interfaces, the container provides either the same or different instances of the service when requested by a class:

public interface IOperation
{
    string OperationId { get; }
}

public interface IOperationTransient : IOperation { }
public interface IOperationScoped : IOperation { }
public interface IOperationSingleton : IOperation { }

La clase Operation siguiente implementa todas las interfaces anteriores.The following Operation class implements all of the preceding interfaces. El constructor Operation genera un GUID y almacena los 4 últimos caracteres en la propiedad OperationId:The Operation constructor generates a GUID and stores the last 4 characters in the OperationId property:

public class Operation : IOperationTransient, IOperationScoped, IOperationSingleton
{
    public Operation()
    {
        OperationId = Guid.NewGuid().ToString()[^4..];
    }

    public string OperationId { get; }
}

El método Startup.ConfigureServices crea varios registros de la clase Operation de acuerdo con las duraciones mencionadas:The Startup.ConfigureServices method creates multiple registrations of the Operation class according to the named lifetimes:

public void ConfigureServices(IServiceCollection services)
{
    services.AddTransient<IOperationTransient, Operation>();
    services.AddScoped<IOperationScoped, Operation>();
    services.AddSingleton<IOperationSingleton, Operation>();

    services.AddRazorPages();
}

La aplicación de ejemplo muestra las duraciones de los objetos dentro y entre las solicitudes.The sample app demonstrates object lifetimes both within and between requests. IndexModel y el middleware solicitan cada clase del tipo IOperation y registran el valor de OperationId de cada una:The IndexModel and the middleware request each kind of IOperation type and log the OperationId for each:

public class IndexModel : PageModel
{
    private readonly ILogger _logger;
    private readonly IOperationTransient _transientOperation;
    private readonly IOperationSingleton _singletonOperation;
    private readonly IOperationScoped _scopedOperation;

    public IndexModel(ILogger<IndexModel> logger,
                      IOperationTransient transientOperation,
                      IOperationScoped scopedOperation,
                      IOperationSingleton singletonOperation)
    {
        _logger = logger;
        _transientOperation = transientOperation;
        _scopedOperation    = scopedOperation;
        _singletonOperation = singletonOperation;
    }

    public void  OnGet()
    {
        _logger.LogInformation("Transient: " + _transientOperation.OperationId);
        _logger.LogInformation("Scoped: "    + _scopedOperation.OperationId);
        _logger.LogInformation("Singleton: " + _singletonOperation.OperationId);
    }
}

De manera similar al IndexModel, el middleware resuelve los mismos servicios:Similar to the IndexModel, the middleware resolves the same services:

public class MyMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger _logger;

    private readonly IOperationTransient _transientOperation;
    private readonly IOperationSingleton _singletonOperation;

    public MyMiddleware(RequestDelegate next, ILogger<MyMiddleware> logger,
        IOperationTransient transientOperation,
        IOperationSingleton singletonOperation)
    {
        _logger = logger;
        _transientOperation = transientOperation;
        _singletonOperation = singletonOperation;
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context,
        IOperationScoped scopedOperation)
    {
        _logger.LogInformation("Transient: " + _transientOperation.OperationId);
        _logger.LogInformation("Scoped: "    + scopedOperation.OperationId);
        _logger.LogInformation("Singleton: " + _singletonOperation.OperationId);

        await _next(context);
    }
}

public static class MyMiddlewareExtensions
{
    public static IApplicationBuilder UseMyMiddleware(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<MyMiddleware>();
    }
}

Los servicios con ámbito se deben resolver en el método InvokeAsync:Scoped services must be resolved in the InvokeAsync method:

public async Task InvokeAsync(HttpContext context,
    IOperationScoped scopedOperation)
{
    _logger.LogInformation("Transient: " + _transientOperation.OperationId);
    _logger.LogInformation("Scoped: "    + scopedOperation.OperationId);
    _logger.LogInformation("Singleton: " + _singletonOperation.OperationId);

    await _next(context);
}

La salida del registrador muestra:The logger output shows:

  • Los objetos Transient siempre son diferentes.Transient objects are always different. El valor OperationId transitorio es distinto en el IndexModel y en el middleware.The transient OperationId value is different in the IndexModel and in the middleware.
  • Los objetos con ámbito son iguales para cada solicitud, pero varían entre solicitudes.Scoped objects are the same for each request but different across each request.
  • Los objetos singleton son los mismos para cada solicitud.Singleton objects are the same for every request.

Para reducir la salida del registro, establezca "Logging:LogLevel:Microsoft:Error" en el archivo appsettings.Development.json:To reduce the logging output, set "Logging:LogLevel:Microsoft:Error" in the appsettings.Development.json file:

{
  "MyKey": "MyKey from appsettings.Developement.json",
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "System": "Debug",
      "Microsoft": "Error"
    }
  }
}

Llamada a servicios desde mainCall services from main

Cree un elemento IServiceScope con IServiceScopeFactory.CreateScope para resolver un servicio con ámbito dentro del ámbito de la aplicación.Create an IServiceScope with IServiceScopeFactory.CreateScope to resolve a scoped service within the app's scope. Este método resulta útil para tener acceso a un servicio con ámbito durante el inicio para realizar tareas de inicialización.This approach is useful to access a scoped service at startup to run initialization tasks.

En el ejemplo siguiente se muestra cómo acceder al servicio IMyDependency con ámbito y llamar a su método WriteMessage en Program.Main:The following example shows how to access the scoped IMyDependency service and call its WriteMessage method in Program.Main:

public class Program
{
    public static void Main(string[] args)
    {
        var host = CreateHostBuilder(args).Build();

        using (var serviceScope = host.Services.CreateScope())
        {
            var services = serviceScope.ServiceProvider;

            try
            {
                var myDependency = services.GetRequiredService<IMyDependency>();
                myDependency.WriteMessage("Call services from main");
            }
            catch (Exception ex)
            {
                var logger = services.GetRequiredService<ILogger<Program>>();
                logger.LogError(ex, "An error occurred.");
            }
        }

        host.Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
}

Validación del ámbitoScope validation

Cuando la aplicación se ejecuta en el entorno de desarrollo y llama a CreateDefaultBuilder para compilar el host, el proveedor de servicios predeterminado realiza comprobaciones para confirmar lo siguiente:When the app runs in the Development environment and calls CreateDefaultBuilder to build the host, the default service provider performs checks to verify that:

  • Los servicios con ámbito no se resuelven desde el proveedor de servicios raíz.Scoped services aren't resolved from the root service provider.
  • Los servicios con ámbito no se insertan en singletons.Scoped services aren't injected into singletons.

El proveedor de servicios raíz se crea cuando se llama a BuildServiceProvider.The root service provider is created when BuildServiceProvider is called. La vigencia del proveedor de servicios raíz es la misma que la de la aplicación cuando el proveedor se inicia con la aplicación, y se elimina cuando la aplicación se cierra.The root service provider's lifetime corresponds to the app's lifetime when the provider starts with the app and is disposed when the app shuts down.

De la eliminación de los servicios con ámbito se encarga el contenedor que los creó.Scoped services are disposed by the container that created them. Si un servicio con ámbito se crea en el contenedor raíz, su duración sube a la del singleton, ya que solo lo puede eliminar el contenedor raíz cuando la aplicación se cierra.If a scoped service is created in the root container, the service's lifetime is effectively promoted to singleton because it's only disposed by the root container when the app shuts down. Al validar los ámbitos de servicio, este tipo de situaciones se detectan cuando se llama a BuildServiceProvider.Validating service scopes catches these situations when BuildServiceProvider is called.

Para más información, vea Validación del ámbito.For more information, see Scope validation.

Servicios de solicitudRequest Services

Los servicios disponibles en una solicitud de ASP.NET Core se exponen mediante la colección HttpContext.RequestServices.The services available within an ASP.NET Core request are exposed through the HttpContext.RequestServices collection. Cuando se solicitan servicios dentro de una solicitud, los servicios y sus dependencias se resuelven desde la colección RequestServices.When services are requested from inside of a request, the services and their dependencies are resolved from the RequestServices collection.

El marco crea un ámbito por solicitud y RequestServices expone el proveedor de servicios con ámbito.The framework creates a scope per request and RequestServices exposes the scoped service provider. Todos los servicios con ámbito son válidos mientras la solicitud está activa.All scoped services are valid for as long as the request is active.

Nota

Se recomienda que solicite las dependencias como parámetros del constructor para resolver los servicios desde la colección RequestServices.Prefer requesting dependencies as constructor parameters to resolving services from the RequestServices collection. Esto genera clases que son más fáciles de probar.This results in classes that are easier to test.

Diseño de servicios para la inserción de dependenciasDesign services for dependency injection

Al diseñar servicios para la inserción de dependencias:When designing services for dependency injection:

  • Evitar clases y miembros estáticos y con estado.Avoid stateful, static classes and members. Evitar crear un estado global mediante el diseño de aplicaciones para usar servicios singleton en su lugar.Avoid creating global state by designing apps to use singleton services instead.
  • Evitar la creación directa de instancias de clases dependientes dentro de los servicios.Avoid direct instantiation of dependent classes within services. La creación directa de instancias se acopla al código de una implementación particular.Direct instantiation couples the code to a particular implementation.
  • Cree servicios pequeños, bien factorizados y probados con facilidad.Make services small, well-factored, and easily tested.

Si una clase tiene muchas dependencias insertadas, podría ser un signo de que la clase tiene demasiadas responsabilidades e infringe el principio de responsabilidad única (SRP).If a class has a lot of injected dependencies, it might be a sign that the class has too many responsibilities and violates the Single Responsibility Principle (SRP). Trate de mover algunas de las responsabilidades de la clase a clases nuevas para intentar refactorizarla.Attempt to refactor the class by moving some of its responsibilities into new classes. Tenga en cuenta que las clases del modelo de página de Razor Pages y las clases del controlador MVC deben centrarse en aspectos de la interfaz de usuario.Keep in mind that Razor Pages page model classes and MVC controller classes should focus on UI concerns.

Eliminación de serviciosDisposal of services

El contenedor llama a Dispose para los tipos IDisposable que crea.The container calls Dispose for the IDisposable types it creates. El desarrollador nunca debe eliminar los servicios resueltos desde el contenedor.Services resolved from the container should never be disposed by the developer. Si un tipo o fábrica se registra como singleton, el contenedor elimina el singleton de manera automática.If a type or factory is registered as a singleton, the container disposes the singleton automatically.

En el ejemplo siguiente, el contenedor de servicios crea los servicios y se eliminan de manera automática:In the following example, the services are created by the service container and disposed automatically:

public class Service1 : IDisposable
{
    private bool _disposed;

    public void Write(string message)
    {
        Console.WriteLine($"Service1: {message}");
    }

    public void Dispose()
    {
        if (_disposed)
            return;

        Console.WriteLine("Service1.Dispose");
        _disposed = true;
    }
}

public class Service2 : IDisposable
{
    private bool _disposed;

    public void Write(string message)
    {
        Console.WriteLine($"Service2: {message}");
    }

    public void Dispose()
    {
        if (_disposed)
            return;

        Console.WriteLine("Service2.Dispose");
        _disposed = true;
    }
}

public interface IService3
{
    public void Write(string message);
}

public class Service3 : IService3, IDisposable
{
    private bool _disposed;

    public Service3(string myKey)
    {
        MyKey = myKey;
    }

    public string MyKey { get; }

    public void Write(string message)
    {
        Console.WriteLine($"Service3: {message}, MyKey = {MyKey}");
    }

    public void Dispose()
    {
        if (_disposed)
            return;

        Console.WriteLine("Service3.Dispose");
        _disposed = true;
    }
}
public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped<Service1>();
    services.AddSingleton<Service2>();
    
    var myKey = Configuration["MyKey"];
    services.AddSingleton<IService3>(sp => new Service3(myKey));

    services.AddRazorPages();
}
public class IndexModel : PageModel
{
    private readonly Service1 _service1;
    private readonly Service2 _service2;
    private readonly IService3 _service3;

    public IndexModel(Service1 service1, Service2 service2, IService3 service3)
    {
        _service1 = service1;
        _service2 = service2;
        _service3 = service3;
    }

    public void OnGet()
    {
        _service1.Write("IndexModel.OnGet");
        _service2.Write("IndexModel.OnGet");
        _service3.Write("IndexModel.OnGet");
    }
}

La consola de depuración muestra la siguiente salida después de cada actualización de la página de índice:The debug console shows the following output after each refresh of the Index page:

Service1: IndexModel.OnGet
Service2: IndexModel.OnGet
Service3: IndexModel.OnGet
Service1.Dispose

Servicios no creados por el contenedor de serviciosServices not created by the service container

Observe el código siguiente:Consider the following code:

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton(new Service1());
    services.AddSingleton(new Service2());

    services.AddRazorPages();
}

En el código anterior:In the preceding code:

  • El contenedor de servicios no crea las instancias de servicio.The service instances aren't created by the service container.
  • El marco de trabajo no elimina los servicios de manera automática.The framework doesn't dispose of the services automatically.
  • El desarrollador es responsable de la eliminación de los servicios.The developer is responsible for disposing the services.

Instrucciones de IDisposable para instancias transitorias y compartidasIDisposable guidance for Transient and shared instances

Instancia transitoria, duración limitadaTransient, limited lifetime

EscenarioScenario

La aplicación requiere una instancia de IDisposable con una duración transitoria para cualquiera de estos escenarios:The app requires an IDisposable instance with a transient lifetime for either of the following scenarios:

  • La instancia se resuelve en el ámbito raíz (contenedor raíz).The instance is resolved in the root scope (root container).
  • La instancia se debe eliminar antes de que finalice el ámbito.The instance should be disposed before the scope ends.

SoluciónSolution

Use el patrón de fábrica para crear una instancia fuera del ámbito principal.Use the factory pattern to create an instance outside of the parent scope. En esta situación, la aplicación habitualmente tendría un método Create que llama directamente al constructor del tipo final.In this situation, the app would generally have a Create method that calls the final type's constructor directly. Si el tipo final tiene otras dependencias, la fábrica puede:If the final type has other dependencies, the factory can:

Instancia compartida, duración limitadaShared instance, limited lifetime

EscenarioScenario

La aplicación requiere una instancia de IDisposable compartida en varios servicios, pero la instancia de IDisposable debe tener una duración limitada.The app requires a shared IDisposable instance across multiple services, but the IDisposable instance should have a limited lifetime.

SoluciónSolution

Registre la instancia con una duración restringida.Register the instance with a scoped lifetime. Use IServiceScopeFactory.CreateScope para crear un IServiceScope nuevo.Use IServiceScopeFactory.CreateScope to create a new IServiceScope. Use el IServiceProvider del ámbito para obtener los servicios necesarios.Use the scope's IServiceProvider to get required services. Elimine el ámbito cuando ya no lo necesite.Dispose the scope when it's no longer needed.

Instrucciones generales de IDisposableGeneral IDisposable guidelines

  • No registre instancias de IDisposable con una duración transitoria.Don't register IDisposable instances with a transient lifetime. En su lugar, use el patrón de fábrica.Use the factory pattern instead.
  • No resuelva instancias de IDisposable con una duración transitoria o restringida en el ámbito raíz.Don't resolve IDisposable instances with a transient or scoped lifetime in the root scope. La única excepción a esto es si la aplicación crea o vuelve a crear y elimina IServiceProvider, pero este no es un patrón ideal.The only exception to this is if the app creates/recreates and disposes IServiceProvider, but this isn't an ideal pattern.
  • La recepción de una dependencia de IDisposable a través de las inserciones de dependencias no requiere que el receptor implemente IDisposable por sí mismo.Receiving an IDisposable dependency via DI doesn't require that the receiver implement IDisposable itself. El receptor de la dependencia de IDisposable no debe llamar a Dispose en esa dependencia.The receiver of the IDisposable dependency shouldn't call Dispose on that dependency.
  • Use ámbitos para controlar las duraciones de los servicios.Use scopes to control the lifetimes of services. Los ámbitos no son jerárquicos y no hay ninguna conexión especial entre ellos.Scopes aren't hierarchical, and there's no special connection among scopes.

Reemplazo del contenedor de servicios predeterminadoDefault service container replacement

El contenedor de servicios integrado está diseñado para atender las necesidades del marco y de la mayoría de las aplicaciones de consumidor.The built-in service container is designed to serve the needs of the framework and most consumer apps. Se recomienda usar el contenedor integrado a menos que se necesite una característica específica no admitida por el contenedor, como:We recommend using the built-in container unless you need a specific feature that it doesn't support, such as:

  • Inserción de propiedadesProperty injection
  • Inserción basada en nombresInjection based on name
  • Contenedores secundariosChild containers
  • Administración personalizada del ciclo de vidaCustom lifetime management
  • Compatibilidad con Func<T> para la inicialización diferidaFunc<T> support for lazy initialization
  • Registro basado en convencionesConvention-based registration

Los siguientes contenedores de terceros se pueden usar con aplicaciones ASP.NET Core:The following third-party containers can be used with ASP.NET Core apps:

Seguridad para subprocesosThread safety

Cree servicios de singleton seguros para subprocesos.Create thread-safe singleton services. Si un servicio de singleton tiene una dependencia en un servicio transitorio, es posible que este también deba ser seguro para subprocesos, según cómo lo use el singleton.If a singleton service has a dependency on a transient service, the transient service may also require thread safety depending on how it's used by the singleton.

El patrón de diseño Factory Method de un servicio único, como el segundo argumento para AddSingleton<TService>(IServiceCollection, Func<IServiceProvider,TService>), no necesita ser seguro para subprocesos.The factory method of single service, such as the second argument to AddSingleton<TService>(IServiceCollection, Func<IServiceProvider,TService>), doesn't need to be thread-safe. Al igual que un constructor de tipos (static), se garantiza que se le llame solo una vez mediante un único subproceso.Like a type (static) constructor, it's guaranteed to be called only once by a single thread.

RecomendacionesRecommendations

  • No se admite la resolución de servicio basada en async/await y Task.async/await and Task based service resolution isn't supported. Como C# no admite constructores asincrónicos, use los métodos asincrónicos después de resolver sincrónicamente el servicio.Because C# doesn't support asynchronous constructors, use asynchronous methods after synchronously resolving the service.

  • Evite almacenar datos y configuraciones directamente en el contenedor de servicios.Avoid storing data and configuration directly in the service container. Por ejemplo, el carro de la compra de un usuario no debería agregarse al contenedor de servicios.For example, a user's shopping cart shouldn't typically be added to the service container. La configuración debe usar el patrón de opciones.Configuration should use the options pattern. Del mismo modo, evite los objetos de tipo "contenedor de datos" que solo existen para permitir el acceso a otro objeto.Similarly, avoid "data holder" objects that only exist to allow access to another object. Es mejor solicitar el elemento real que se necesita mediante la inserción de dependencias.It's better to request the actual item via DI.

  • Evite el acceso estático a los servicios.Avoid static access to services. Por ejemplo, evite capturar IApplicationBuilder.ApplicationServices como campo estático o propiedad para su uso en otra parte.For example, avoid capturing IApplicationBuilder.ApplicationServices as a static field or property for use elsewhere.

  • Mantenga los generadores de DI rápidos y sincrónicos.Keep DI factories fast and synchronous.

  • Evite el uso del patrón del localizador de servicios.Avoid using the service locator pattern. Por ejemplo, no invoque a GetService para obtener una instancia de servicio si puede usar la inserción de dependencias en su lugar:For example, don't invoke GetService to obtain a service instance when you can use DI instead:

    Incorrecto:Incorrect:

    Código incorrecto

    Correcto:Correct:

    public class MyClass
    {
        private readonly IOptionsMonitor<MyOptions> _optionsMonitor;
    
        public MyClass(IOptionsMonitor<MyOptions> optionsMonitor)
        {
            _optionsMonitor = optionsMonitor;
        }
    
        public void MyMethod()
        {
            var option = _optionsMonitor.CurrentValue.Option;
    
            ...
        }
    }
    
  • Otra variación del localizador de servicios que se debe evitar es insertar una fábrica que resuelva dependencias en tiempo de ejecución.Another service locator variation to avoid is injecting a factory that resolves dependencies at runtime. Estas dos prácticas combinan estrategias de Inversión de control.Both of these practices mix Inversion of Control strategies.

  • Evite el acceso estático a HttpContext (por ejemplo, IHttpContextAccessor.HttpContext).Avoid static access to HttpContext (for example, IHttpContextAccessor.HttpContext).

  • Evite las llamadas a BuildServiceProvider en ConfigureServices.Avoid calls to BuildServiceProvider in ConfigureServices. La llamada a BuildServiceProvider suele ocurrir cuando el desarrollador desea resolver un servicio en ConfigureServices.Calling BuildServiceProvider typically happens when the developer wants to resolve a service in ConfigureServices. Por ejemplo, considere el caso donde LoginPath se carga desde la configuración.For example, consider the case where the LoginPath is loaded from configuration. Evite el enfoque siguiente:Avoid the following approach:

    código incorrecto llamando a BuildServiceProvider

    En la imagen anterior, al seleccionar la línea verde ondulada debajo de services.BuildServiceProvider se muestra la siguiente advertencia ASP0000:In the preceding image, selecting the green wavy line under services.BuildServiceProvider shows the following ASP0000 warning:

    ASP0000 Al llamar a "BuildServiceProvider" desde el código de aplicación, se crea una copia adicional de los servicios singleton.ASP0000 Calling 'BuildServiceProvider' from application code results in an additional copy of singleton services being created. Considere alternativas como los servicios de inserción de dependencias como parámetros para "Configurar".Consider alternatives such as dependency injecting services as parameters to 'Configure'.

    La llamada a BuildServiceProvider crea un segundo contenedor que puede crear singletons rasgados y producir referencias a gráficos de objetos en varios contenedores.Calling BuildServiceProvider creates a second container, which can create torn singletons and cause references to object graphs across multiple containers.

    Una manera correcta de obtener LoginPath es usar la compatibilidad integrada del patrón de opciones para DI:A correct way to get LoginPath is to use the options pattern's built-in support for DI:

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
            .AddCookie();
    
        services.AddOptions<CookieAuthenticationOptions>(
                            CookieAuthenticationDefaults.AuthenticationScheme)
            .Configure<IMyService>((options, myService) =>
            {
                options.LoginPath = myService.GetLoginPath();
            });
    
        services.AddRazorPages();
    }
    
  • El contenedor captura a los servicios transitorios descartables para su eliminación.Disposable transient services are captured by the container for disposal. Esto puede convertirse en una pérdida de memoria si se resuelve desde el contenedor de nivel superior.This can turn into a memory leak if resolved from the top level container.

  • Habilite la validación con ámbito para asegurarse de que la aplicación no tenga singletons que capturen los servicios con ámbito.Enable scope validation to make sure the app doesn't have singletons that capture scoped services. Para más información, vea Validación del ámbito.For more information, see Scope validation.

Al igual que sucede con todas las recomendaciones, podría verse en una situación que le obligue a ignorar alguna de ellas.Like all sets of recommendations, you may encounter situations where ignoring a recommendation is required. Las excepciones son poco frecuentes, principalmente en casos especiales dentro del marco de trabajo mismo.Exceptions are rare, mostly special cases within the framework itself.

La inserción de dependencias es una alternativa a los patrones de acceso a objetos estáticos o globales.DI is an alternative to static/global object access patterns. No podrá aprovechar las ventajas de la inserción de dependencias si la combina con el acceso a objetos estáticos.You may not be able to realize the benefits of DI if you mix it with static object access.

Orchard Core es un marco de trabajo de la aplicación para compilar aplicaciones modulares y multiinquilino en ASP.NET Core.Orchard Core is an application framework for building modular, multi-tenant applications on ASP.NET Core. Para obtener más información, vea la documentación de Orchard Core.For more information, see the Orchard Core Documentation.

Consulte los ejemplos de Orchard Core para obtener ejemplos sobre cómo compilar aplicaciones modulares y multiinquilino mediante el uso exclusivo del marco de Orchard Core, sin ninguna de las características específicas de CMS.See the Orchard Core samples for examples of how to build modular and multi-tenant apps using just the Orchard Core Framework without any of its CMS-specific features.

Servicios proporcionados por el marco de trabajoFramework-provided services

El método Startup.ConfigureServices registra los servicios que la aplicación usa, incluidas las características de plataforma, como Entity Framework Core y ASP.NET Core MVC.The Startup.ConfigureServices method registers services that the app uses, including platform features, such as Entity Framework Core and ASP.NET Core MVC. Inicialmente, el valor IServiceCollection proporcionado a ConfigureServices tiene los servicios definidos por el marco en función de cómo se configurara el host.Initially, the IServiceCollection provided to ConfigureServices has services defined by the framework depending on how the host was configured. En el caso de las aplicaciones basadas en las plantillas de ASP.NET Core, el marco de trabajo registra más de 250 servicios.For apps based on the ASP.NET Core templates, the framework registers more than 250 services.

En la tabla siguiente se ilustra una pequeña muestra de estos servicios registrados por el marco:The following table lists a small sample of these framework-registered services:

Tipo de servicioService Type Período de duraciónLifetime
Microsoft.AspNetCore.Hosting.Builder.IApplicationBuilderFactory TransitorioTransient
IHostApplicationLifetime SingletonSingleton
IWebHostEnvironment SingletonSingleton
Microsoft.AspNetCore.Hosting.IStartup SingletonSingleton
Microsoft.AspNetCore.Hosting.IStartupFilter TransitorioTransient
Microsoft.AspNetCore.Hosting.Server.IServer SingletonSingleton
Microsoft.AspNetCore.Http.IHttpContextFactory TransitorioTransient
Microsoft.Extensions.Logging.ILogger<TCategoryName> SingletonSingleton
Microsoft.Extensions.Logging.ILoggerFactory SingletonSingleton
Microsoft.Extensions.ObjectPool.ObjectPoolProvider SingletonSingleton
Microsoft.Extensions.Options.IConfigureOptions<TOptions> TransitorioTransient
Microsoft.Extensions.Options.IOptions<TOptions> SingletonSingleton
System.Diagnostics.DiagnosticSource SingletonSingleton
System.Diagnostics.DiagnosticListener SingletonSingleton

Recursos adicionalesAdditional resources

Por Steve Smith, Scott Addie y Brandon DahlerBy Steve Smith, Scott Addie, and Brandon Dahler

ASP.NET Core admite el patrón de diseño de software de inserción de dependencias (DI), que es una técnica para conseguir la inversión de control (IoC) entre clases y sus dependencias.ASP.NET Core supports the dependency injection (DI) software design pattern, which is a technique for achieving Inversion of Control (IoC) between classes and their dependencies.

Para más información específica sobre la inserción de dependencias en los controladores MVC, vea Inserción de dependencias en controladores en ASP.NET Core.For more information specific to dependency injection within MVC controllers, see Inserción de dependencias en controladores en ASP.NET Core.

Vea o descargue el código de ejemplo (cómo descargarlo)View or download sample code (how to download)

Información general sobre la inserción de dependenciasOverview of dependency injection

Una dependencia es cualquier objeto requerido por otro objeto.A dependency is any object that another object requires. Examine la siguiente clase MyDependency con un método WriteMessage del que dependen otras clases de una aplicación:Examine the following MyDependency class with a WriteMessage method that other classes in an app depend upon:

public class MyDependency
{
    public MyDependency()
    {
    }

    public Task WriteMessage(string message)
    {
        Console.WriteLine(
            $"MyDependency.WriteMessage called. Message: {message}");

        return Task.FromResult(0);
    }
}

Se puede crear una instancia de la clase MyDependency para hacer que el método WriteMessage esté disponible para una clase.An instance of the MyDependency class can be created to make the WriteMessage method available to a class. La clase MyDependency es una dependencia de la clase IndexModel:The MyDependency class is a dependency of the IndexModel class:

public class IndexModel : PageModel
{
    MyDependency _dependency = new MyDependency();

    public async Task OnGetAsync()
    {
        await _dependency.WriteMessage(
            "IndexModel.OnGetAsync created this message.");
    }
}

La clase crea y depende directamente de la instancia MyDependency.The class creates and directly depends on the MyDependency instance. Las dependencias de código (como en el ejemplo anterior) son problemáticas y deben evitarse por las siguientes razones:Code dependencies (such as the previous example) are problematic and should be avoided for the following reasons:

  • Para reemplazar MyDependency con una implementación diferente, se debe modificar la clase.To replace MyDependency with a different implementation, the class must be modified.
  • Si MyDependency tiene dependencias, deben configurarse según la clase.If MyDependency has dependencies, they must be configured by the class. En un proyecto grande con varias clases que dependen de MyDependency, el código de configuración se dispersa por la aplicación.In a large project with multiple classes depending on MyDependency, the configuration code becomes scattered across the app.
  • Esta implementación es difícil para realizar pruebas unitarias.This implementation is difficult to unit test. La aplicación debe usar una clase MyDependency como boceto o código auxiliar, que no es posible con este enfoque.The app should use a mock or stub MyDependency class, which isn't possible with this approach.

La inserción de dependencias aborda estos problemas mediante:Dependency injection addresses these problems through:

  • Uso de una interfaz o clase base para abstraer la implementación de dependencias.The use of an interface or base class to abstract the dependency implementation.
  • Registro de la dependencia en un contenedor de servicios.Registration of the dependency in a service container. ASP.NET Core proporciona un contenedor de servicios integrado, IServiceProvider.ASP.NET Core provides a built-in service container, IServiceProvider. Los servicios se registran en el método Startup.ConfigureServices de la aplicación.Services are registered in the app's Startup.ConfigureServices method.
  • Inserción del servicio en el constructor de la clase en la que se usa.Injection of the service into the constructor of the class where it's used. El marco de trabajo asume la responsabilidad de crear una instancia de la dependencia y de desecharla cuando ya no es necesaria.The framework takes on the responsibility of creating an instance of the dependency and disposing of it when it's no longer needed.

En la aplicación de ejemplo, la interfaz IMyDependency define un método que el servicio proporciona a la aplicación:In the sample app, the IMyDependency interface defines a method that the service provides to the app:

public interface IMyDependency
{
    Task WriteMessage(string message);
}

Esta interfaz se implementa mediante un tipo concreto, MyDependency:This interface is implemented by a concrete type, MyDependency:

public class MyDependency : IMyDependency
{
    private readonly ILogger<MyDependency> _logger;

    public MyDependency(ILogger<MyDependency> logger)
    {
        _logger = logger;
    }

    public Task WriteMessage(string message)
    {
        _logger.LogInformation(
            "MyDependency.WriteMessage called. Message: {Message}", 
            message);

        return Task.FromResult(0);
    }
}

MyDependency solicita ILogger<TCategoryName> en su constructor.MyDependency requests an ILogger<TCategoryName> in its constructor. No es raro usar la inserción de dependencias de forma encadenada.It's not unusual to use dependency injection in a chained fashion. Cada dependencia solicitada a su vez solicita sus propias dependencias.Each requested dependency in turn requests its own dependencies. El contenedor resuelve las dependencias del gráfico y devuelve el servicio totalmente resuelto.The container resolves the dependencies in the graph and returns the fully resolved service. El conjunto colectivo de dependencias que deben resolverse suele denominarse árbol de dependencias, gráfico de dependencias o gráfico de objetos.The collective set of dependencies that must be resolved is typically referred to as a dependency tree, dependency graph, or object graph.

IMyDependency y ILogger<TCategoryName> deben estar registrados en el contenedor de servicios.IMyDependency and ILogger<TCategoryName> must be registered in the service container. IMyDependency está registrado en Startup.ConfigureServices.IMyDependency is registered in Startup.ConfigureServices. ILogger<TCategoryName> está registrado en la infraestructura de abstracciones de registros, por lo que se trata de un servicio proporcionado por el marco de trabajo registrado de forma predeterminada por el marco de trabajo.ILogger<TCategoryName> is registered by the logging abstractions infrastructure, so it's a framework-provided service registered by default by the framework.

El contenedor resuelve ILogger<TCategoryName> aprovechando las ventajas de los tipos abiertos (genéricos), lo que elimina la necesidad de registrar todos los tipos construidos (genéricos):The container resolves ILogger<TCategoryName> by taking advantage of (generic) open types, eliminating the need to register every (generic) constructed type:

services.AddSingleton(typeof(ILogger<>), typeof(Logger<>));

En la aplicación de ejemplo, el servicio IMyDependency está registrado con el tipo concreto MyDependency.In the sample app, the IMyDependency service is registered with the concrete type MyDependency. El registro abarca la duración del servicio como la duración de una única solicitud.The registration scopes the service lifetime to the lifetime of a single request. Las duraciones del servicio se describen más adelante en este tema.Service lifetimes are described later in this topic.

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

    services.AddScoped<IMyDependency, MyDependency>();
    services.AddTransient<IOperationTransient, Operation>();
    services.AddScoped<IOperationScoped, Operation>();
    services.AddSingleton<IOperationSingleton, Operation>();
    services.AddSingleton<IOperationSingletonInstance>(new Operation(Guid.Empty));

    // OperationService depends on each of the other Operation types.
    services.AddTransient<OperationService, OperationService>();
}

Nota

Cada método de extensión services.Add{SERVICE_NAME} agrega servicios (y potencialmente los configura).Each services.Add{SERVICE_NAME} extension method adds (and potentially configures) services. Por ejemplo, services.AddMvc() agrega los servicios que Razor Pages y MVC requieren.For example, services.AddMvc() adds the services Razor Pages and MVC require. Se recomienda que las aplicaciones sigan esta convención.We recommended that apps follow this convention. Coloque los métodos de extensión en el espacio de nombres Microsoft.Extensions.DependencyInjection para encapsular grupos de registros del servicio.Place extension methods in the Microsoft.Extensions.DependencyInjection namespace to encapsulate groups of service registrations.

Si el constructor del servicio requiere un tipo integrado, como string, se puede insertar mediante la configuración o el patrón de opciones:If the service's constructor requires a built in type, such as a string, the type can be injected by using configuration or the options pattern:

public class MyDependency : IMyDependency
{
    public MyDependency(IConfiguration config)
    {
        var myStringValue = config["MyStringKey"];

        // Use myStringValue
    }

    ...
}

Se solicita una instancia del servicio mediante el constructor de una clase, en la que se usa el servicio y se asigna a un campo privado.An instance of the service is requested via the constructor of a class where the service is used and assigned to a private field. El campo de utiliza para acceder al servicio, según sea necesario en la clase.The field is used to access the service as necessary throughout the class.

En la aplicación de ejemplo, la instancia IMyDependency se solicita y usa para llamar al método WriteMessage del servicio:In the sample app, the IMyDependency instance is requested and used to call the service's WriteMessage method:

public class IndexModel : PageModel
{
    private readonly IMyDependency _myDependency;

    public IndexModel(
        IMyDependency myDependency, 
        OperationService operationService,
        IOperationTransient transientOperation,
        IOperationScoped scopedOperation,
        IOperationSingleton singletonOperation,
        IOperationSingletonInstance singletonInstanceOperation)
    {
        _myDependency = myDependency;
        OperationService = operationService;
        TransientOperation = transientOperation;
        ScopedOperation = scopedOperation;
        SingletonOperation = singletonOperation;
        SingletonInstanceOperation = singletonInstanceOperation;
    }

    public OperationService OperationService { get; }
    public IOperationTransient TransientOperation { get; }
    public IOperationScoped ScopedOperation { get; }
    public IOperationSingleton SingletonOperation { get; }
    public IOperationSingletonInstance SingletonInstanceOperation { get; }

    public async Task OnGetAsync()
    {
        await _myDependency.WriteMessage(
            "IndexModel.OnGetAsync created this message.");
    }
}

Servicios insertados en StartupServices injected into Startup

Solo se pueden insertar los tipos de servicio siguientes en el constructor Startup cuando se usa el host genérico (IHostBuilder):Only the following service types can be injected into the Startup constructor when using the Generic Host (IHostBuilder):

Los servicios se pueden insertar en Startup.Configure:Services can be injected into Startup.Configure:

public void Configure(IApplicationBuilder app, IOptions<MyOptions> options)
{
    ...
}

Para obtener más información, vea Inicio de la aplicación en ASP.NET Core.For more information, see Inicio de la aplicación en ASP.NET Core.

Servicios proporcionados por el marco de trabajoFramework-provided services

El método Startup.ConfigureServices se encarga de definir los servicios que la aplicación usa, incluidas las características de plataforma, como Entity Framework Core y ASP.NET Core MVC.The Startup.ConfigureServices method is responsible for defining the services that the app uses, including platform features, such as Entity Framework Core and ASP.NET Core MVC. Inicialmente, el valor IServiceCollection proporcionado a ConfigureServices tiene los servicios definidos por el marco en función de cómo se configurara el host.Initially, the IServiceCollection provided to ConfigureServices has services defined by the framework depending on how the host was configured. No es raro que una aplicación basada en una plantilla de ASP.NET Core tenga cientos de servicios registrados por el marco.It's not uncommon for an app based on an ASP.NET Core template to have hundreds of services registered by the framework. En la tabla siguiente se ilustra una pequeña muestra de servicios registrados por el marco.A small sample of framework-registered services is listed in the following table.

Tipo de servicioService Type Período de duraciónLifetime
Microsoft.AspNetCore.Hosting.Builder.IApplicationBuilderFactory TransitorioTransient
Microsoft.AspNetCore.Hosting.IApplicationLifetime SingletonSingleton
Microsoft.AspNetCore.Hosting.IHostingEnvironment SingletonSingleton
Microsoft.AspNetCore.Hosting.IStartup SingletonSingleton
Microsoft.AspNetCore.Hosting.IStartupFilter TransitorioTransient
Microsoft.AspNetCore.Hosting.Server.IServer SingletonSingleton
Microsoft.AspNetCore.Http.IHttpContextFactory TransitorioTransient
Microsoft.Extensions.Logging.ILogger<TCategoryName> SingletonSingleton
Microsoft.Extensions.Logging.ILoggerFactory SingletonSingleton
Microsoft.Extensions.ObjectPool.ObjectPoolProvider SingletonSingleton
Microsoft.Extensions.Options.IConfigureOptions<TOptions> TransitorioTransient
Microsoft.Extensions.Options.IOptions<TOptions> SingletonSingleton
System.Diagnostics.DiagnosticSource SingletonSingleton
System.Diagnostics.DiagnosticListener SingletonSingleton

Registro de servicios adicionales con métodos de extensiónRegister additional services with extension methods

Cuando un método de extensión de la colección de servicio está disponible para registrar un servicio (y sus servicios dependientes, si es necesario), la convención consiste en usar un solo método de extensión Add{SERVICE_NAME} para registrar todos los servicios requeridos por dicho servicio.When a service collection extension method is available to register a service (and its dependent services, if required), the convention is to use a single Add{SERVICE_NAME} extension method to register all of the services required by that service. El código siguiente es un ejemplo de cómo agregar servicios adicionales al contenedor mediante los métodos de extensión AddDbContext<TContext> y AddIdentityCore:The following code is an example of how to add additional services to the container using the extension methods AddDbContext<TContext> and AddIdentityCore:

public void ConfigureServices(IServiceCollection services)
{
    ...

    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

    services.AddIdentity<ApplicationUser, IdentityRole>()
        .AddEntityFrameworkStores<ApplicationDbContext>()
        .AddDefaultTokenProviders();

    ...
}

Para obtener más información, consulte la clase ServiceCollection en la documentación de la API.For more information, see the ServiceCollection class in the API documentation.

Duraciones de serviciosService lifetimes

Elija una duración adecuada para cada servicio registrado.Choose an appropriate lifetime for each registered service. Los servicios de ASP.NET Core pueden configurarse con las duraciones siguientes:ASP.NET Core services can be configured with the following lifetimes:

TransitorioTransient

Los servicios de duración transitoria (AddTransient) se crean cada vez que el contenedor del servicio los solicita.Transient lifetime services (AddTransient) are created each time they're requested from the service container. Esta duración funciona mejor para servicios sin estado ligeros.This lifetime works best for lightweight, stateless services.

En las aplicaciones que procesan solicitudes, los servicios transitorios se eliminan al final de la solicitud.In apps that process requests, transient services are disposed at the end of the request.

Con ámbitoScoped

Los servicios de duración con ámbito (AddScoped) se crean una vez por solicitud del cliente (conexión).Scoped lifetime services (AddScoped) are created once per client request (connection).

En las aplicaciones que procesan solicitudes, los servicios con ámbito se eliminan al final de la solicitud.In apps that process requests, scoped services are disposed at the end of the request.

Advertencia

Si usa un servicio con ámbito en un middleware, inserte el servicio en el método Invoke o InvokeAsync.When using a scoped service in a middleware, inject the service into the Invoke or InvokeAsync method. No lo inserte a través de la inserción de constructores, porque ello hace que el servicio se comporte como un singleton.Don't inject via constructor injection because it forces the service to behave like a singleton. Para obtener más información, vea Escritura de middleware de ASP.NET Core personalizado.For more information, see Escritura de middleware de ASP.NET Core personalizado.

SingletonSingleton

Los servicios con duración Singleton (AddSingleton) se crean la primera vez que se solicitan, o bien al ejecutar Startup.ConfigureServices y especificar una instancia con el registro del servicio.Singleton lifetime services (AddSingleton) are created the first time they're requested (or when Startup.ConfigureServices is run and an instance is specified with the service registration). Cada solicitud posterior usa la misma instancia.Every subsequent request uses the same instance. Si la aplicación requiere un comportamiento de singleton, se recomienda permitir que el contenedor de servicios administre la duración del servicio.If the app requires singleton behavior, allowing the service container to manage the service's lifetime is recommended. No implemente el patrón de diseño de singleton y proporcione el código de usuario para administrar la duración del objeto en la clase.Don't implement the singleton design pattern and provide user code to manage the object's lifetime in the class.

En las aplicaciones que procesan solicitudes, los servicios singleton se eliminan cuando ServiceProvider se elimina al cerrarse la aplicación.In apps that process requests, singleton services are disposed when the ServiceProvider is disposed at app shutdown.

Advertencia

Es peligroso resolver un servicio con ámbito desde un singleton.It's dangerous to resolve a scoped service from a singleton. Puede dar lugar a que el servicio adopte un estado incorrecto al procesar solicitudes posteriores.It may cause the service to have incorrect state when processing subsequent requests.

Métodos de registro del servicioService registration methods

Los métodos de extensión de registro del servicio ofrecen sobrecargas útiles en escenarios específicos.Service registration extension methods offer overloads that are useful in specific scenarios.

MétodoMethod AutomáticoAutomatic
objetoobject
eliminacióndisposal
MúltipleMultiple
implementacionesimplementations
Transferencia de argumentosPass args
Add{LIFETIME}<{SERVICE}, {IMPLEMENTATION}>()
Ejemplo:Example:
services.AddSingleton<IMyDep, MyDep>();
Yes Yes NoNo
Add{LIFETIME}<{SERVICE}>(sp => new {IMPLEMENTATION})
Ejemplos:Examples:
services.AddSingleton<IMyDep>(sp => new MyDep());
services.AddSingleton<IMyDep>(sp => new MyDep("A string!"));
Yes Yes Yes
Add{LIFETIME}<{IMPLEMENTATION}>()
Ejemplo:Example:
services.AddSingleton<MyDep>();
Yes NoNo NoNo
AddSingleton<{SERVICE}>(new {IMPLEMENTATION})
Ejemplos:Examples:
services.AddSingleton<IMyDep>(new MyDep());
services.AddSingleton<IMyDep>(new MyDep("A string!"));
NoNo Yes Yes
AddSingleton(new {IMPLEMENTATION})
Ejemplos:Examples:
services.AddSingleton(new MyDep());
services.AddSingleton(new MyDep("A string!"));
NoNo NoNo Yes

Para obtener más información sobre el tipo de eliminación, consulte la sección Eliminación de servicios.For more information on type disposal, see the Disposal of services section. Un escenario común para varias implementaciones es utilizar tipos de simulación para las pruebas.A common scenario for multiple implementations is mocking types for testing.

Los métodos TryAdd{LIFETIME} solo registran el servicio si no hay ya una implementación registrada.TryAdd{LIFETIME} methods only register the service if there isn't already an implementation registered.

En el ejemplo siguiente, la primera línea registra MyDependency para IMyDependency.In the following example, the first line registers MyDependency for IMyDependency. La segunda línea no tiene ningún efecto porque IMyDependency ya tiene una implementación registrada:The second line has no effect because IMyDependency already has a registered implementation:

services.AddSingleton<IMyDependency, MyDependency>();
// The following line has no effect:
services.TryAddSingleton<IMyDependency, DifferentDependency>();

Para obtener más información, consulte:For more information, see:

Los métodos TryAddEnumerable(ServiceDescriptor) registran solo el servicio si no hay ya una implementación del mismo tipo.TryAddEnumerable(ServiceDescriptor) methods only register the service if there isn't already an implementation of the same type. A través de IEnumerable<{SERVICE}> se resuelven varios servicios.Multiple services are resolved via IEnumerable<{SERVICE}>. Al registrar los servicios, el desarrollador solo quiere agregar una instancia si no se ha agregado ya una del mismo tipo.When registering services, the developer only wants to add an instance if one of the same type hasn't already been added. Por lo general, este método lo utilizan los creadores de bibliotecas para evitar registrar dos copias de una instancia en el contenedor.Generally, this method is used by library authors to avoid registering two copies of an instance in the container.

En el ejemplo siguiente, la primera línea registra MyDep para IMyDep1.In the following example, the first line registers MyDep for IMyDep1. La segunda línea registra MyDep para IMyDep2.The second line registers MyDep for IMyDep2. La tercera línea no tiene ningún efecto porque IMyDep1 ya tiene una implementación registrada de MyDep:The third line has no effect because IMyDep1 already has a registered implementation of MyDep:

public interface IMyDep1 {}
public interface IMyDep2 {}

public class MyDep : IMyDep1, IMyDep2 {}

services.TryAddEnumerable(ServiceDescriptor.Singleton<IMyDep1, MyDep>());
services.TryAddEnumerable(ServiceDescriptor.Singleton<IMyDep2, MyDep>());
// Two registrations of MyDep for IMyDep1 is avoided by the following line:
services.TryAddEnumerable(ServiceDescriptor.Singleton<IMyDep1, MyDep>());

Comportamiento de inserción de constructorConstructor injection behavior

Los servicios se pueden resolver mediante dos mecanismos:Services can be resolved by two mechanisms:

  • IServiceProvider
  • ActivatorUtilities: permite la creación de objetos sin registrar el servicio en el contenedor de inserción de dependencias.ActivatorUtilities: Permits object creation without service registration in the dependency injection container. ActivatorUtilities se utiliza con abstracciones orientadas al usuario, como asistentes de etiquetas, controladores MVC y enlazadores de modelos.ActivatorUtilities is used with user-facing abstractions, such as Tag Helpers, MVC controllers, and model binders.

Los constructores pueden aceptar argumentos que no se proporcionan mediante la inserción de dependencias, pero los argumentos deben asignar valores predeterminados.Constructors can accept arguments that aren't provided by dependency injection, but the arguments must assign default values.

Cuando se resuelven los servicios mediante IServiceProvider o ActivatorUtilities, la inserción del constructor requiere un constructor público.When services are resolved by IServiceProvider or ActivatorUtilities, constructor injection requires a public constructor.

Cuando se resuelven los servicios mediante ActivatorUtilities, la inserción del constructor requiere que exista solo un constructor aplicable.When services are resolved by ActivatorUtilities, constructor injection requires that only one applicable constructor exists. Se admiten las sobrecargas de constructor, pero solo puede existir una sobrecarga cuyos argumentos pueda cumplir la inserción de dependencias.Constructor overloads are supported, but only one overload can exist whose arguments can all be fulfilled by dependency injection.

Contextos de Entity FrameworkEntity Framework contexts

Los contextos de Entity Framework normalmente se agregan al contenedor de servicios mediante la duración con ámbito porque las operaciones de base de datos de aplicación web se suelen limitar a la solicitud de cliente.Entity Framework contexts are usually added to the service container using the scoped lifetime because web app database operations are normally scoped to the client request. La duración predeterminada se limita si no se especifica una duración mediante una sobrecarga de AddDbContext<TContext> al registrar el contexto de base de datos.The default lifetime is scoped if a lifetime isn't specified by an AddDbContext<TContext> overload when registering the database context. En los servicios de una duración determinada no se debe usar un contexto de base de datos con una duración más corta que el servicio.Services of a given lifetime shouldn't use a database context with a shorter lifetime than the service.

Opciones de registro y duraciónLifetime and registration options

Para mostrar la diferencia entre la duración y las opciones de registro, considere las siguientes interfaces que representan tareas como una operación con un identificador único, OperationId.To demonstrate the difference between the lifetime and registration options, consider the following interfaces that represent tasks as an operation with a unique identifier, OperationId. Según cómo esté configurada la duración de un servicio de operaciones para las interfaces siguientes, el contenedor proporciona la misma instancia del servicio u otra distinta cuando así lo solicita la clase:Depending on how the lifetime of an operations service is configured for the following interfaces, the container provides either the same or a different instance of the service when requested by a class:

public interface IOperation
{
    Guid OperationId { get; }
}

public interface IOperationTransient : IOperation
{
}

public interface IOperationScoped : IOperation
{
}

public interface IOperationSingleton : IOperation
{
}

public interface IOperationSingletonInstance : IOperation
{
}

Las interfaces se implementan en la clase Operation.The interfaces are implemented in the Operation class. El constructor Operation genera un GUID en caso de que no se proporcione uno:The Operation constructor generates a GUID if one isn't supplied:

public class Operation : IOperationTransient, 
    IOperationScoped, 
    IOperationSingleton, 
    IOperationSingletonInstance
{
    public Operation() : this(Guid.NewGuid())
    {
    }

    public Operation(Guid id)
    {
        OperationId = id;
    }

    public Guid OperationId { get; private set; }
}

Se registra una instancia de OperationService que depende de cada uno de los demás tipos Operation.An OperationService is registered that depends on each of the other Operation types. Cuando OperationService se solicita con la inserción de dependencias, recibe una instancia nueva de cada servicio o una instancia existente en función de la duración de los servicios dependientes.When OperationService is requested via dependency injection, it receives either a new instance of each service or an existing instance based on the lifetime of the dependent service.

  • Si se crean servicios transitorios cuando se solicitan al contenedor, el elemento OperationId del servicio IOperationTransient es diferente del objeto OperationId de OperationService.When transient services are created when requested from the container, the OperationId of the IOperationTransient service is different than the OperationId of the OperationService. OperationService recibe una instancia nueva de la clase IOperationTransient.OperationService receives a new instance of the IOperationTransient class. La nueva instancia produce un objeto OperationId diferente.The new instance yields a different OperationId.
  • Si se crean servicios con ámbito por solicitud de cliente, el objeto OperationId del servicio IOperationScoped es el mismo que para OperationService dentro de la solicitud de cliente.When scoped services are created per client request, the OperationId of the IOperationScoped service is the same as that of OperationService within a client request. Entre las solicitudes de cliente, ambos servicios comparten un valor OperationId diferente.Across client requests, both services share a different OperationId value.
  • Si se crean servicios singleton y servicios de instancia singleton una vez y se usan en todas las solicitudes de cliente y servicios, el objeto OperationId es constante en todas las solicitudes de servicio.When singleton and singleton-instance services are created once and used across all client requests and all services, the OperationId is constant across all service requests.
public class OperationService
{
    public OperationService(
        IOperationTransient transientOperation,
        IOperationScoped scopedOperation,
        IOperationSingleton singletonOperation,
        IOperationSingletonInstance instanceOperation)
    {
        TransientOperation = transientOperation;
        ScopedOperation = scopedOperation;
        SingletonOperation = singletonOperation;
        SingletonInstanceOperation = instanceOperation;
    }

    public IOperationTransient TransientOperation { get; }
    public IOperationScoped ScopedOperation { get; }
    public IOperationSingleton SingletonOperation { get; }
    public IOperationSingletonInstance SingletonInstanceOperation { get; }
}

En Startup.ConfigureServices, cada tipo se agrega al contenedor según su duración con nombre:In Startup.ConfigureServices, each type is added to the container according to its named lifetime:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

    services.AddScoped<IMyDependency, MyDependency>();
    services.AddTransient<IOperationTransient, Operation>();
    services.AddScoped<IOperationScoped, Operation>();
    services.AddSingleton<IOperationSingleton, Operation>();
    services.AddSingleton<IOperationSingletonInstance>(new Operation(Guid.Empty));

    // OperationService depends on each of the other Operation types.
    services.AddTransient<OperationService, OperationService>();
}

El servicio IOperationSingletonInstance usa una instancia específica con un identificador conocido de Guid.Empty.The IOperationSingletonInstance service is using a specific instance with a known ID of Guid.Empty. Resulta evidente identificar cuándo este tipo está en uso (su GUID es todo ceros).It's clear when this type is in use (its GUID is all zeroes).

La aplicación de ejemplo muestra las duraciones de los objetos dentro y entre las solicitudes individuales.The sample app demonstrates object lifetimes within and between individual requests. El objeto IndexModel de la aplicación de ejemplo solicita cada tipo de IOperation y OperationService.The sample app's IndexModel requests each kind of IOperation type and the OperationService. Después, la página muestra todos los valores OperationId del servicio y la clase del modelo de página mediante las asignaciones de propiedades:The page then displays all of the page model class's and service's OperationId values through property assignments:

public class IndexModel : PageModel
{
    private readonly IMyDependency _myDependency;

    public IndexModel(
        IMyDependency myDependency, 
        OperationService operationService,
        IOperationTransient transientOperation,
        IOperationScoped scopedOperation,
        IOperationSingleton singletonOperation,
        IOperationSingletonInstance singletonInstanceOperation)
    {
        _myDependency = myDependency;
        OperationService = operationService;
        TransientOperation = transientOperation;
        ScopedOperation = scopedOperation;
        SingletonOperation = singletonOperation;
        SingletonInstanceOperation = singletonInstanceOperation;
    }

    public OperationService OperationService { get; }
    public IOperationTransient TransientOperation { get; }
    public IOperationScoped ScopedOperation { get; }
    public IOperationSingleton SingletonOperation { get; }
    public IOperationSingletonInstance SingletonInstanceOperation { get; }

    public async Task OnGetAsync()
    {
        await _myDependency.WriteMessage(
            "IndexModel.OnGetAsync created this message.");
    }
}

Las dos salidas siguientes muestran el resultado de dos solicitudes:Two following output shows the results of two requests:

Primera solicitud:First request:

Operaciones del controlador:Controller operations:

Transient: d233e165-f417-469b-a866-1cf1935d2518Transient: d233e165-f417-469b-a866-1cf1935d2518
Scoped: 5d997e2d-55f5-4a64-8388-51c4e3a1ad19Scoped: 5d997e2d-55f5-4a64-8388-51c4e3a1ad19
Singleton: 01271bc1-9e31-48e7-8f7c-7261b040ded9Singleton: 01271bc1-9e31-48e7-8f7c-7261b040ded9
Instance: 00000000-0000-0000-0000-000000000000Instance: 00000000-0000-0000-0000-000000000000

Operaciones OperationService:OperationService operations:

Transient: c6b049eb-1318-4e31-90f1-eb2dd849ff64Transient: c6b049eb-1318-4e31-90f1-eb2dd849ff64
Scoped: 5d997e2d-55f5-4a64-8388-51c4e3a1ad19Scoped: 5d997e2d-55f5-4a64-8388-51c4e3a1ad19
Singleton: 01271bc1-9e31-48e7-8f7c-7261b040ded9Singleton: 01271bc1-9e31-48e7-8f7c-7261b040ded9
Instance: 00000000-0000-0000-0000-000000000000Instance: 00000000-0000-0000-0000-000000000000

Segunda solicitud:Second request:

Operaciones del controlador:Controller operations:

Transient: b63bd538-0a37-4ff1-90ba-081c5138dda0Transient: b63bd538-0a37-4ff1-90ba-081c5138dda0
Scoped: 31e820c5-4834-4d22-83fc-a60118acb9f4Scoped: 31e820c5-4834-4d22-83fc-a60118acb9f4
Singleton: 01271bc1-9e31-48e7-8f7c-7261b040ded9Singleton: 01271bc1-9e31-48e7-8f7c-7261b040ded9
Instance: 00000000-0000-0000-0000-000000000000Instance: 00000000-0000-0000-0000-000000000000

Operaciones OperationService:OperationService operations:

Transitorio: c4cbacb8-36a2-436d-81c8-8c1b78808aafTransient: c4cbacb8-36a2-436d-81c8-8c1b78808aaf
Scoped: 31e820c5-4834-4d22-83fc-a60118acb9f4Scoped: 31e820c5-4834-4d22-83fc-a60118acb9f4
Singleton: 01271bc1-9e31-48e7-8f7c-7261b040ded9Singleton: 01271bc1-9e31-48e7-8f7c-7261b040ded9
Instance: 00000000-0000-0000-0000-000000000000Instance: 00000000-0000-0000-0000-000000000000

Observe cuál de los valores OperationId varía dentro de una solicitud y entre solicitudes:Observe which of the OperationId values vary within a request and between requests:

  • Los objetos Transient siempre son diferentes.Transient objects are always different. El valor OperationId transitorio de la primera y la segunda solicitud de cliente varía tanto para las operaciones OperationService como entre solicitudes de cliente.The transient OperationId value for both the first and second client requests are different for both OperationService operations and across client requests. Se proporciona una nueva instancia para cada solicitud de servicio y solicitud de cliente.A new instance is provided to each service request and client request.
  • Los objetos con ámbito son iguales dentro de una solicitud de cliente, pero varían entre solicitudes de cliente.Scoped objects are the same within a client request but different across client requests.
  • Los objetos singleton son iguales para todos los objetos y solicitudes, independientemente de si se proporciona una instancia Operation en Startup.ConfigureServices.Singleton objects are the same for every object and every request regardless of whether an Operation instance is provided in Startup.ConfigureServices.

Llamada a servicios desde mainCall services from main

Cree un elemento IServiceScope con IServiceScopeFactory.CreateScope para resolver un servicio con ámbito dentro del ámbito de la aplicación.Create an IServiceScope with IServiceScopeFactory.CreateScope to resolve a scoped service within the app's scope. Este método resulta útil para tener acceso a un servicio con ámbito durante el inicio para realizar tareas de inicialización.This approach is useful to access a scoped service at startup to run initialization tasks. En el siguiente ejemplo se indica cómo obtener un contexto para MyScopedService en Program.Main:The following example shows how to obtain a context for the MyScopedService in Program.Main:

using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

public class Program
{
    public static async Task Main(string[] args)
    {
        var host = CreateWebHostBuilder(args).Build();

        using (var serviceScope = host.Services.CreateScope())
        {
            var services = serviceScope.ServiceProvider;

            try
            {
                var serviceContext = services.GetRequiredService<MyScopedService>();
                // Use the context here
            }
            catch (Exception ex)
            {
                var logger = services.GetRequiredService<ILogger<Program>>();
                logger.LogError(ex, "An error occurred.");
            }
        }

        await host.RunAsync();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>();
}

Validación del ámbitoScope validation

Cuando la aplicación se ejecuta en el entorno de desarrollo, el proveedor de servicios predeterminado realiza comprobaciones para confirmar lo siguiente:When the app is running in the Development environment, the default service provider performs checks to verify that:

  • Los servicios con ámbito no se resuelven directa o indirectamente desde el proveedor de servicios raíz.Scoped services aren't directly or indirectly resolved from the root service provider.
  • Los servicios con ámbito no se insertan directa o indirectamente en singletons.Scoped services aren't directly or indirectly injected into singletons.

El proveedor de servicios raíz se crea cuando se llama a BuildServiceProvider.The root service provider is created when BuildServiceProvider is called. La vigencia del proveedor de servicios raíz es la misma que la de la aplicación o el servidor cuando el proveedor se inicia con la aplicación, y se elimina cuando la aplicación se cierra.The root service provider's lifetime corresponds to the app/server's lifetime when the provider starts with the app and is disposed when the app shuts down.

De la eliminación de los servicios con ámbito se encarga el contenedor que los creó.Scoped services are disposed by the container that created them. Si un servicio con ámbito se crea en el contenedor raíz, su vigencia sube a la del singleton, ya que solo lo puede eliminar el contenedor raíz cuando la aplicación o el servidor se cierran.If a scoped service is created in the root container, the service's lifetime is effectively promoted to singleton because it's only disposed by the root container when app/server is shut down. Al validar los ámbitos de servicio, este tipo de situaciones se detectan cuando se llama a BuildServiceProvider.Validating service scopes catches these situations when BuildServiceProvider is called.

Para obtener más información, vea Host web de ASP.NET Core.For more information, see Host web de ASP.NET Core.

Servicios de solicitudRequest Services

Los servicios disponibles en una solicitud de ASP.NET Core desde HttpContext se exponen mediante la colección HttpContext.RequestServices.The services available within an ASP.NET Core request from HttpContext are exposed through the HttpContext.RequestServices collection.

Los servicios de solicitud representan los servicios configurados y solicitados como parte de la aplicación.Request Services represent the services configured and requested as part of the app. Cuando los objetos especifican dependencias, estas se cumplen mediante los tipos que se encuentran en RequestServices, no en ApplicationServices.When the objects specify dependencies, these are satisfied by the types found in RequestServices, not ApplicationServices.

Por lo general, la aplicación no debe usar estas propiedades directamente.Generally, the app shouldn't use these properties directly. En su lugar, solicite los tipos que las clases requieren mediante el constructor de clases y permita que el marco de trabajo inserte las dependencias.Instead, request the types that classes require via class constructors and allow the framework inject the dependencies. Esto produce clases que son más fáciles de probar.This yields classes that are easier to test.

Nota

Se recomienda que solicite las dependencias como parámetros del constructor para obtener acceso a la colección RequestServices.Prefer requesting dependencies as constructor parameters to accessing the RequestServices collection.

Diseño de servicios para la inserción de dependenciasDesign services for dependency injection

Los procedimientos recomendados son:Best practices are to:

  • Diseñar servicios para usar la inserción de dependencias a fin de obtener sus dependencias.Design services to use dependency injection to obtain their dependencies.
  • Evitar clases y miembros estáticos y con estado.Avoid stateful, static classes and members. En su lugar, diseñe las aplicaciones para usar servicios Singleton, lo que evita crear un estado global.Design apps to use singleton services instead, which avoid creating global state.
  • Evitar la creación directa de instancias de clases dependientes dentro de los servicios.Avoid direct instantiation of dependent classes within services. La creación directa de instancias se acopla al código de una implementación particular.Direct instantiation couples the code to a particular implementation.
  • Cree clases de aplicación pequeñas, bien factorizadas y probadas con facilidad.Make app classes small, well-factored, and easily tested.

Si una clase parece tener demasiadas dependencias insertadas, suele indicar que la clase tiene demasiadas responsabilidades y que esto infringe el principio de responsabilidad única (SRP).If a class seems to have too many injected dependencies, this is generally a sign that the class has too many responsibilities and is violating the Single Responsibility Principle (SRP). Trate de mover algunas de las responsabilidades de la clase a una nueva para intentar refactorizarla.Attempt to refactor the class by moving some of its responsibilities into a new class. Tenga en cuenta que las clases del modelo de página de Razor Pages y las clases del controlador MVC deben centrarse en aspectos de la interfaz de usuario.Keep in mind that Razor Pages page model classes and MVC controller classes should focus on UI concerns. Los detalles de implementación de las reglas de negocio y del acceso a datos se deben mantener en las clases pertinentes para cada uno de estos aspectos.Business rules and data access implementation details should be kept in classes appropriate to these separate concerns.

Eliminación de serviciosDisposal of services

El contenedor llama a Dispose para los tipos IDisposable que crea.The container calls Dispose for the IDisposable types it creates. Si se agrega una instancia al contenedor por código de usuario, no se elimina automáticamente.If an instance is added to the container by user code, it isn't disposed automatically.

En el ejemplo siguiente, el contenedor de servicios crea los servicios y se eliminan de manera automática:In the following example, the services are created by the service container and disposed automatically:

public class Service1 : IDisposable {}
public class Service2 : IDisposable {}

public interface IService3 {}
public class Service3 : IService3, IDisposable {}

public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped<Service1>();
    services.AddSingleton<Service2>();
    services.AddSingleton<IService3>(sp => new Service3());
}

En el ejemplo siguiente:In the following example:

  • El contenedor de servicios no crea las instancias de servicio.The service instances aren't created by the service container.
  • El marco de trabajo no conoce la duración prevista del servicio.The intended service lifetimes aren't known by the framework.
  • El marco de trabajo no elimina los servicios de manera automática.The framework doesn't dispose of the services automatically.
  • Si los servicios no se eliminan explícitamente en el código de desarrollador, se conservan hasta que se cierra la aplicación.If the services aren't explicitly disposed in developer code, they persist until the app shuts down.
public class Service1 : IDisposable {}
public class Service2 : IDisposable {}

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<Service1>(new Service1());
    services.AddSingleton(new Service2());
}

Instrucciones de IDisposable para instancias transitorias y compartidasIDisposable guidance for Transient and shared instances

Instancia transitoria, duración limitadaTransient, limited lifetime

EscenarioScenario

La aplicación requiere una instancia de IDisposable con una duración transitoria para cualquiera de estos escenarios:The app requires an IDisposable instance with a transient lifetime for either of the following scenarios:

  • La instancia se resuelve en el ámbito raíz.The instance is resolved in the root scope.
  • La instancia se debe eliminar antes de que finalice el ámbito.The instance should be disposed before the scope ends.

SoluciónSolution

Use el patrón de fábrica para crear una instancia fuera del ámbito principal.Use the factory pattern to create an instance outside of the parent scope. En esta situación, la aplicación habitualmente tendría un método Create que llama directamente al constructor del tipo final.In this situation, the app would generally have a Create method that calls the final type's constructor directly. Si el tipo final tiene otras dependencias, la fábrica puede:If the final type has other dependencies, the factory can:

Instancia compartida, duración limitadaShared Instance, limited lifetime

EscenarioScenario

La aplicación requiere una instancia de IDisposable compartida en varios servicios, pero IDisposable debe tener una duración limitada.The app requires a shared IDisposable instance across multiple services, but the IDisposable should have a limited lifetime.

SoluciónSolution

Registre la instancia con una duración restringida.Register the instance with a Scoped lifetime. Use IServiceScopeFactory.CreateScope para iniciar y crear un IServiceScope nuevo.Use IServiceScopeFactory.CreateScope to start and create a new IServiceScope. Use el IServiceProvider del ámbito para obtener los servicios necesarios.Use the scope's IServiceProvider to get required services. Elimine el ámbito cuando la duración deba finalizar.Dispose the scope when the lifetime should end.

Instrucciones generalesGeneral Guidelines

  • No registre instancias de IDisposable con un ámbito transitorio.Don't register IDisposable instances with a Transient scope. En su lugar, use el patrón de fábrica.Use the factory pattern instead.
  • No resuelva instancias de IDisposable transitorias ni restringidas en el ámbito raíz.Don't resolve Transient or Scoped IDisposable instances in the root scope. La única excepción general es cuando la aplicación crea o vuelve a crear el IServiceProvider o lo elimina, lo que no es un patrón ideal.The only general exception is when the app creates/recreates and disposes the IServiceProvider, which isn't an ideal pattern.
  • La recepción de una dependencia de IDisposable a través de las inserciones de dependencias no requiere que el receptor implemente IDisposable por sí mismo.Receiving an IDisposable dependency via DI doesn't require that the receiver implement IDisposable itself. El receptor de la dependencia de IDisposable no debe llamar a Dispose en esa dependencia.The receiver of the IDisposable dependency shouldn't call Dispose on that dependency.
  • Se deben usar ámbitos para controlar la duración de los servicios.Scopes should be used to control lifetimes of services. Los ámbitos no son jerárquicos y no hay ninguna conexión especial entre ellos.Scopes aren't hierarchical, and there's no special connection among scopes.

Reemplazo del contenedor de servicios predeterminadoDefault service container replacement

El contenedor de servicios integrado está diseñado para atender las necesidades del marco y de la mayoría de las aplicaciones de consumidor.The built-in service container is designed to serve the needs of the framework and most consumer apps. Se recomienda usar el contenedor integrado a menos que necesite una característica específica que el contenedor integrado no admite, como las siguientes:We recommend using the built-in container unless you need a specific feature that the built-in container doesn't support, such as:

  • Inserción de propiedadesProperty injection
  • Inserción basada en nombresInjection based on name
  • Contenedores secundariosChild containers
  • Administración personalizada del ciclo de vidaCustom lifetime management
  • Compatibilidad con Func<T> para la inicialización diferidaFunc<T> support for lazy initialization
  • Registro basado en convencionesConvention-based registration

Los siguientes contenedores de terceros se pueden usar con aplicaciones ASP.NET Core:The following third-party containers can be used with ASP.NET Core apps:

Seguridad para subprocesosThread safety

Cree servicios de singleton seguros para subprocesos.Create thread-safe singleton services. Si un servicio de singleton tiene una dependencia en un servicio transitorio, es posible que este también deba ser seguro para subprocesos, según cómo lo use el singleton.If a singleton service has a dependency on a transient service, the transient service may also require thread safety depending how it's used by the singleton.

El patrón de diseño Factory Method de un servicio único, como el segundo argumento para AddSingleton<TService>(IServiceCollection, Func<IServiceProvider,TService>), no necesita ser seguro para subprocesos.The factory method of single service, such as the second argument to AddSingleton<TService>(IServiceCollection, Func<IServiceProvider,TService>), doesn't need to be thread-safe. Al igual que un constructor de tipos (static), se garantiza que se le llame una vez mediante un único subproceso.Like a type (static) constructor, it's guaranteed to be called once by a single thread.

RecomendacionesRecommendations

  • No se admite la resolución de servicio basada en async/await y Task.async/await and Task based service resolution is not supported. C# no admite los constructores asincrónicos, por lo que el patrón recomendado es usar métodos asincrónicos después de resolver el servicio de manera sincrónica.C# does not support asynchronous constructors; therefore, the recommended pattern is to use asynchronous methods after synchronously resolving the service.

  • Evite almacenar datos y configuraciones directamente en el contenedor de servicios.Avoid storing data and configuration directly in the service container. Por ejemplo, el carro de la compra de un usuario no debería agregarse al contenedor de servicios.For example, a user's shopping cart shouldn't typically be added to the service container. La configuración debe usar el patrón de opciones.Configuration should use the options pattern. Del mismo modo, evite los objetos de tipo "contenedor de datos" que solo existen para permitir el acceso a otro objeto.Similarly, avoid "data holder" objects that only exist to allow access to some other object. Es mejor solicitar el elemento real que se necesita mediante la inserción de dependencias.It's better to request the actual item via DI.

  • Evite el acceso estático a los servicios.Avoid static access to services. Por ejemplo, evite escribir de forma estática IApplicationBuilder.ApplicationServices para usarlo en otro lugar.For example, avoid statically-typing IApplicationBuilder.ApplicationServices for use elsewhere.

  • Evite usar el patrón de localizador de servicios, que combina las estrategias de Inversión de control.Avoid using the service locator pattern, which mixes Inversion of Control strategies.

    • No invoque GetService para obtener una instancia de servicio cuando pueda usar la inserción de dependencias en su lugar:Don't invoke GetService to obtain a service instance when you can use DI instead:

      Incorrecto:Incorrect:

      public class MyClass()
      
        public void MyMethod()
        {
            var optionsMonitor = 
                _services.GetService<IOptionsMonitor<MyOptions>>();
            var option = optionsMonitor.CurrentValue.Option;
      
            ...
        }
      

      Correcto:Correct:

      public class MyClass
      {
          private readonly IOptionsMonitor<MyOptions> _optionsMonitor;
      
          public MyClass(IOptionsMonitor<MyOptions> optionsMonitor)
          {
              _optionsMonitor = optionsMonitor;
          }
      
          public void MyMethod()
          {
              var option = _optionsMonitor.CurrentValue.Option;
      
              ...
          }
      }
      
  • Evite insertar una fábrica que resuelva las dependencias en tiempo de ejecución con GetService.Avoid injecting a factory that resolves dependencies at runtime using GetService.

  • Evite el acceso estático a HttpContext (por ejemplo, IHttpContextAccessor.HttpContext).Avoid static access to HttpContext (for example, IHttpContextAccessor.HttpContext).

Al igual que sucede con todas las recomendaciones, podría verse en una situación que le obligue a ignorar alguna de ellas.Like all sets of recommendations, you may encounter situations where ignoring a recommendation is required. Las excepciones son poco frecuentes, principalmente en casos especiales dentro del marco de trabajo mismo.Exceptions are rare, mostly special cases within the framework itself.

La inserción de dependencias es una alternativa a los patrones de acceso a objetos estáticos o globales.DI is an alternative to static/global object access patterns. No podrá aprovechar las ventajas de la inserción de dependencias si la combina con el acceso a objetos estáticos.You may not be able to realize the benefits of DI if you mix it with static object access.

Recursos adicionalesAdditional resources