Compartir a través de


Inicio rápido: Conceptos básicos de inserción de dependencias en .NET

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:

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 greeting dado el name.
  • Llama al método WriteLine en la instancia IConsole.
  • Devuelve el greeting al 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 ServiceCollection instancia.
  • Registre y configure servicios en ServiceCollection:
    • El uso de IConsole la sobrecarga del generador de implementación devuelve un tipo DefaultConsole con IsEnabled establecido en true.
    • IGreetingService se agrega con un tipo correspondiente de implementación de DefaultGreetingService.
    • FarewellService se agrega como un tipo concreto.
  • Construir el ServiceProvider a partir del ServiceCollection.
  • Resuelva los servicios IGreetingService y FarewellService.
  • 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.

Consulte también