Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
En esta guía de inicio rápido, creará una aplicación de consola .NET que crea manualmente un ServiceCollection y un ServiceProvider correspondiente. Aprenderás a registrar servicios y resolverlos mediante la inyección de dependencias (DI). En este artículo se usa el paquete NuGet Microsoft.Extensions.DependencyInjection para demostrar los conceptos básicos de la inyección de dependencias en .NET.
Nota:
En este artículo no se aprovechan las características del host genérico . Para obtener una guía más completa, consulte Uso de la inserción de dependencias en .NET.
Comienza
Para empezar, cree una nueva aplicación de consola de .NET denominada DI.Basics. En la lista siguiente se hace referencia a algunos de los enfoques más comunes para crear un proyecto de consola:
- Visual Studio: menú Archivo > Nuevo > Proyecto.
- Visual Studio Code y la opción de menú del C# Dev Kit: Explorador de soluciones.
-
CLI de .NET:
dotnet new consolecomando en el terminal.
Debe agregar la referencia de paquete a Microsoft.Extensions.DependencyInjection en el archivo del proyecto. Independientemente del enfoque, asegúrese de que el proyecto es similar al siguiente XML del archivo DI.Basics.csproj :
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.2" />
</ItemGroup>
</Project>
Conceptos básicos de inserción de dependencias
La inserción de dependencias es un patrón de diseño que permite quitar dependencias codificadas de forma rígida y hacer que la aplicación sea más fácil de mantener y probar. DI es una técnica para lograr la inversión de control (IoC) entre las clases y sus dependencias.
Las abstracciones para di en .NET se definen en el paquete NuGet Microsoft.Extensions.DependencyInjection.Abstractions :
- IServiceCollection: define un contrato para una colección de descriptores de servicio.
- IServiceProvider: define un mecanismo para recuperar un objeto de servicio.
- ServiceDescriptor: describe un servicio con su tipo de servicio, implementación y duración.
En .NET, la inyección de dependencia (DI) se gestiona mediante la adición y configuración de servicios en un IServiceCollection. Después de registrar los servicios, se construye una instancia llamando al método BuildServiceProvider.
IServiceProvider actúa como contenedor de todos los servicios registrados y se usa para resolver los servicios.
Creación de servicios de ejemplo
No todos los servicios se crean de la misma manera. Algunos servicios necesitan una nueva instancia cada vez que el contenedor de servicios los obtiene (transitorio), mientras que otros deben compartirse entre solicitudes (con alcance) o durante toda la duración de la aplicación (singleton). Para obtener más información sobre la duración del servicio, consulte Vigencias del servicio.
Del mismo modo, algunos servicios solo exponen un tipo concreto, mientras que otros se expresan como un contrato entre una interfaz y un tipo de implementación. Puede crear varias variaciones de servicios para ayudar a demostrar estos conceptos.
Cree un nuevo archivo de C# denominado IConsole.cs y agregue el código siguiente:
public interface IConsole
{
void WriteLine(string message);
}
Este archivo define una IConsole interfaz que expone un único método, WriteLine. A continuación, cree un nuevo archivo de C# denominado DefaultConsole.cs y agregue el código siguiente:
internal sealed class DefaultConsole : IConsole
{
public bool IsEnabled { get; set; } = true;
void IConsole.WriteLine(string message)
{
if (IsEnabled is false)
{
return;
}
Console.WriteLine(message);
}
}
El código anterior representa la implementación predeterminada de la IConsole interfaz. El método WriteLine escribe en la consola de manera condicional según la propiedad IsEnabled.
Sugerencia
La nomenclatura de una implementación es una opción que el equipo de desarrollo debe aceptar. El Default prefijo es una convención común para indicar una implementación predeterminada de una interfaz, pero no es necesaria.
A continuación, cree un archivo IGreetingService.cs y agregue el siguiente código de C#:
public interface IGreetingService
{
string Greet(string name);
}
A continuación, agregue un nuevo archivo de C# denominado DefaultGreetingService.cs y agregue el código siguiente:
internal sealed class DefaultGreetingService(
IConsole console) : IGreetingService
{
public string Greet(string name)
{
var greeting = $"Hello, {name}!";
console.WriteLine(greeting);
return greeting;
}
}
El código anterior representa la implementación predeterminada de la IGreetingService interfaz. La implementación del servicio requiere un IConsole como parámetro de constructor principal. El método Greet realiza las acciones siguientes:
- Crea un
greetingdado elname. - Llama al método
WriteLineen la instanciaIConsole. - Devuelve el
greetingal llamante.
El último servicio que se va a crear es el archivo FarewellService.cs , agregue el siguiente código de C# antes de continuar:
public class FarewellService(IConsole console)
{
public string SayGoodbye(string name)
{
var farewell = $"Goodbye, {name}!";
console.WriteLine(farewell);
return farewell;
}
}
FarewellService representa un tipo concreto, no una interfaz. Debe declararse como public para que sea accesible para los consumidores. A diferencia de otros tipos de implementación de servicio que se declararon como internal y sealed, este código demuestra que no todos los servicios deben ser interfaces. También muestra que las implementaciones de servicio pueden ser sealed para evitar la herencia y internal para restringir el acceso al ensamblado.
Actualización de la Program clase
Abra el archivo Program.cs y reemplace el código existente por el siguiente código de C#:
using Microsoft.Extensions.DependencyInjection;
// 1. Create the service collection.
var services = new ServiceCollection();
// 2. Register (add and configure) the services.
services.AddSingleton<IConsole>(
implementationFactory: static _ => new DefaultConsole
{
IsEnabled = true
});
services.AddSingleton<IGreetingService, DefaultGreetingService>();
services.AddSingleton<FarewellService>();
// 3. Build the service provider from the service collection.
var serviceProvider = services.BuildServiceProvider();
// 4. Resolve the services that you need.
var greetingService = serviceProvider.GetRequiredService<IGreetingService>();
var farewellService = serviceProvider.GetRequiredService<FarewellService>();
// 5. Use the services
var greeting = greetingService.Greet("David");
var farewell = farewellService.SayGoodbye("David");
El código actualizado anterior muestra el procedimiento:
- Cree una nueva
ServiceCollectioninstancia. - Registre y configure servicios en
ServiceCollection:- El uso de
IConsolela sobrecarga del generador de implementación devuelve un tipoDefaultConsoleconIsEnabledestablecido entrue. -
IGreetingServicese agrega con un tipo correspondiente de implementación deDefaultGreetingService. -
FarewellServicese agrega como un tipo concreto.
- El uso de
- Construir el
ServiceProvidera partir delServiceCollection. - Resuelva los servicios
IGreetingServiceyFarewellService. - Utilice los servicios disponibles para saludar y despedirse de una persona denominada
David.
Si actualiza la propiedad IsEnabled de DefaultConsole a false, los métodos Greet y SayGoodbye omiten la escritura de los mensajes resultantes en la consola. Un cambio similar a este, ayuda a demostrar que el IConsole servicio se inserta en los IGreetingService servicios y FarewellService como una dependencia que influye en ese comportamiento de las aplicaciones.
Todos estos servicios se registran como singletons, aunque para este ejemplo, funciona de forma idéntica si se registran como servicios transitorios o de ámbito.
Importante
En este ejemplo artificial, las duraciones de los servicios no importan, pero en una aplicación real, debe considerar detenidamente la duración de cada uno de los servicios.
Ejecutar la aplicación de ejemplo
Para ejecutar la aplicación de ejemplo, presione F5 en Visual Studio o Visual Studio Code o ejecute el dotnet run comando en el terminal. Cuando se complete la aplicación, debería ver la siguiente salida:
Hello, David!
Goodbye, David!
Descriptores de servicio
Las API más usadas para agregar servicios a ServiceCollection son métodos de extensión genéricos con nombre de duración, como:
AddSingleton<TService>AddTransient<TService>AddScoped<TService>
Estos métodos son métodos útiles que crean una ServiceDescriptor instancia y lo agregan a ServiceCollection.
ServiceDescriptor es una clase sencilla que describe un servicio con su tipo de servicio, tipo de implementación y duración. También puede describir las factorías de implementación y las instancias.
Para cada uno de los servicios que ha registrado en ServiceCollection, podría llamar al método Add con una instancia de ServiceDescriptor directamente. Tenga en cuenta los ejemplos siguientes:
services.Add(ServiceDescriptor.Describe(
serviceType: typeof(IConsole),
implementationFactory: static _ => new DefaultConsole
{
IsEnabled = true
},
lifetime: ServiceLifetime.Singleton));
El código anterior es equivalente a cómo se registró el servicio IConsole en el ServiceCollection. El Add método se usa para agregar una ServiceDescriptor instancia que describe el IConsole servicio. El método ServiceDescriptor.Describe estático se delega en varios ServiceDescriptor constructores. Considere el código equivalente para el IGreetingService servicio:
services.Add(ServiceDescriptor.Describe(
serviceType: typeof(IGreetingService),
implementationType: typeof(DefaultGreetingService),
lifetime: ServiceLifetime.Singleton));
El código anterior describe el IGreetingService servicio con su tipo de servicio, el tipo de implementación y la duración. Por último, considere el código equivalente para el FarewellService servicio:
services.Add(ServiceDescriptor.Describe(
serviceType: typeof(FarewellService),
implementationType: typeof(FarewellService),
lifetime: ServiceLifetime.Singleton));
En el código anterior se describe el tipo concreto FarewellService como el servicio y los tipos de implementación. El servicio se registra como un servicio singleton.