En aplicaciones modernas, Dependency Injection es un componente clave para construir sistemas desacoplados, testeables y escalables. Sin embargo, en WinUI 3 su implementación no siempre es evidente, lo que lleva a soluciones improvisadas o inconsistentes.

En muchos proyectos, se observa una mezcla de instanciación manual con patrones incompletos de inyección de dependencias, lo que introduce acoplamiento y dificulta la evolución del sistema.

Este artículo aborda cómo implementar Dependency Injection correctamente en WinUI 3, con un enfoque orientado a aplicaciones reales.

El problema Link to heading

Uno de los errores más comunes es evitar el uso de un contenedor de dependencias y crear instancias manualmente.

Errores típicos:

  • Instanciación directa en la vista
  • Dependencias ocultas
  • Dificultad para testear
  • Código rígido y difícil de mantener

Ejemplo incorrecto Link to heading

public sealed partial class MainWindow : Window
{
    public MainWindow()
    {
        this.InitializeComponent();

        var service = new DataService();
        var vm = new MainViewModel(service);

        this.DataContext = vm;
    }
}

Problemas:

  • Dependencias acopladas
  • Difícil de modificar
  • No escalable

La solución Link to heading

Dependency Injection permite desacoplar la creación de objetos de su uso.

Principios:

  1. Invertir dependencias
  2. Centralizar configuración
  3. Facilitar testing
  4. Mejorar mantenibilidad

Paso 1: Agregar contenedor de servicios Link to heading

using Microsoft.Extensions.DependencyInjection;

public partial class App : Application
{
    public static IServiceProvider Services { get; private set; }

    public App()
    {
        var collection = new ServiceCollection();
        ConfigureServices(collection);

        Services = collection.BuildServiceProvider();

        this.InitializeComponent();
    }

    private void ConfigureServices(IServiceCollection services)
    {
        services.AddSingleton<IDataService, DataService>();
        services.AddTransient<MainViewModel>();
    }
}

Esto define el contenedor global.

Paso 2: Resolver dependencias Link to heading

public sealed partial class MainWindow : Window
{
    public MainViewModel ViewModel { get; }

    public MainWindow()
    {
        this.InitializeComponent();

        ViewModel = App.Services.GetService<MainViewModel>();
        this.DataContext = this;
    }
}

Esto elimina instanciación manual.

Paso 3: Inyección en ViewModel Link to heading

public class MainViewModel
{
    private readonly IDataService _service;

    public MainViewModel(IDataService service)
    {
        _service = service;
    }
}

El ViewModel no conoce la implementación concreta.

Paso 4: Ciclos de vida Link to heading

  • Singleton: instancia única
  • Transient: nueva instancia por solicitud
  • Scoped: no común en desktop

Ejemplo:

services.AddSingleton<AppState>();
services.AddTransient<DetailsViewModel>();

Elegir correctamente es clave.

Paso 5: Inyección en páginas Link to heading

public sealed partial class DetailsPage : Page
{
    public DetailsViewModel ViewModel { get; }

    public DetailsPage()
    {
        this.InitializeComponent();

        ViewModel = App.Services.GetService<DetailsViewModel>();
        this.DataContext = this;
    }
}

Esto mantiene consistencia.

Paso 6: Testing Link to heading

Dependency Injection permite reemplazar implementaciones.

var mockService = new Mock<IDataService>();
var vm = new MainViewModel(mockService.Object);

Esto facilita pruebas unitarias.

Paso 7: Errores comunes en producción Link to heading

  • Registrar demasiados singletons
  • Mezclar instanciación manual con DI
  • No centralizar configuración
  • Dependencias innecesarias

Solución:

  • Mantener el contenedor simple
  • Revisar ciclos de vida
  • Evitar dependencias implícitas

Buenas prácticas Link to heading

  • Usar un único contenedor
  • Definir servicios mediante interfaces
  • Mantener configuración centralizada
  • Evitar instanciación manual
  • Diseñar pensando en testing

Conclusión Link to heading

Dependency Injection en WinUI 3 es un elemento fundamental para construir aplicaciones profesionales. Su correcta implementación permite desacoplar componentes, mejorar la mantenibilidad y facilitar la evolución del sistema.

Ignorar este patrón o aplicarlo de forma parcial conduce a aplicaciones rígidas y difíciles de escalar.