# Dependency Injection Setup / Cấu Hình DI

Hướng dẫn chi tiết về Dependency Injection trong .NET MAUI với MauiProgram.cs.

## Core Concepts / Khái Niệm Cốt Lõi

1. **Built-in DI**: MAUI sử dụng `Microsoft.Extensions.DependencyInjection`
2. **Service Lifetimes**: Singleton, Scoped, Transient
3. **Constructor Injection**: Inject dependencies qua constructor

---

## MauiProgram.cs Structure

```csharp
public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        
        builder
            .UseMauiApp<App>()
            .UseMauiCommunityToolkit()
            .ConfigureFonts(fonts =>
            {
                fonts.AddFont("Inter-Regular.ttf", "InterRegular");
                fonts.AddFont("Inter-SemiBold.ttf", "InterSemiBold");
                fonts.AddFont("Inter-Bold.ttf", "InterBold");
            });

        // Register services
        RegisterServices(builder.Services);
        
        // Register ViewModels
        RegisterViewModels(builder.Services);
        
        // Register Views
        RegisterViews(builder.Services);

#if DEBUG
        builder.Logging.AddDebug();
#endif

        return builder.Build();
    }
    
    private static void RegisterServices(IServiceCollection services)
    {
        // HTTP Client
        services.AddHttpClient<IApiClient, ApiClient>(client =>
        {
            client.BaseAddress = new Uri("https://api.example.com/");
            client.Timeout = TimeSpan.FromSeconds(30);
        });
        
        // Singleton: Shared state, expensive to create
        services.AddSingleton<ISettingsService, SettingsService>();
        services.AddSingleton<IAuthService, AuthService>();
        services.AddSingleton<INavigationService, NavigationService>();
        services.AddSingleton<IConnectivityService, ConnectivityService>();
        
        // Transient: Stateless, lightweight
        services.AddTransient<IProductService, ProductService>();
        services.AddTransient<IOrderService, OrderService>();
    }
    
    private static void RegisterViewModels(IServiceCollection services)
    {
        // Transient: New instance per navigation
        services.AddTransient<MainViewModel>();
        services.AddTransient<ProductListViewModel>();
        services.AddTransient<ProductDetailViewModel>();
        services.AddTransient<CartViewModel>();
        services.AddTransient<CheckoutViewModel>();
        services.AddTransient<SettingsViewModel>();
    }
    
    private static void RegisterViews(IServiceCollection services)
    {
        // Transient: Match ViewModel lifecycle
        services.AddTransient<MainPage>();
        services.AddTransient<ProductListPage>();
        services.AddTransient<ProductDetailPage>();
        services.AddTransient<CartPage>();
        services.AddTransient<CheckoutPage>();
        services.AddTransient<SettingsPage>();
    }
}
```

---

## Service Lifetimes

### When to Use Each Lifetime

| Lifetime | When to Use | Examples |
|----------|-------------|----------|
| **Singleton** | Shared state across app | AuthService, SettingsService, Cache |
| **Scoped** | Per-request (rarely used in MAUI) | DbContext in Blazor Hybrid |
| **Transient** | Stateless, new each time | ViewModels, Pages, Repositories |

### Singleton Services

```csharp
// ✅ Singleton: Giữ state toàn cục
public interface IAuthService
{
    User? CurrentUser { get; }
    bool IsAuthenticated { get; }
    Task<bool> LoginAsync(string email, string password);
    Task LogoutAsync();
}

public class AuthService : IAuthService
{
    private User? _currentUser;
    
    public User? CurrentUser => _currentUser;
    public bool IsAuthenticated => _currentUser != null;
    
    public async Task<bool> LoginAsync(string email, string password)
    {
        _currentUser = await _apiClient.LoginAsync(email, password);
        return _currentUser != null;
    }
    
    public Task LogoutAsync()
    {
        _currentUser = null;
        return Task.CompletedTask;
    }
}

// Registration
services.AddSingleton<IAuthService, AuthService>();
```

### Transient ViewModels

```csharp
// ✅ Transient: Mỗi lần navigate tạo instance mới
services.AddTransient<ProductDetailViewModel>();

// ❌ SAI: Singleton ViewModel sẽ giữ data cũ
services.AddSingleton<ProductDetailViewModel>(); // DON'T!
```

---

## View-ViewModel Injection

### Constructor Injection Pattern

```csharp
// View nhận ViewModel qua constructor
public partial class ProductListPage : ContentPage
{
    public ProductListPage(ProductListViewModel viewModel)
    {
        InitializeComponent();
        BindingContext = viewModel;
    }
}

// ViewModel nhận Services qua constructor
public partial class ProductListViewModel : ObservableObject
{
    private readonly IProductService _productService;
    private readonly INavigationService _navigation;
    
    public ProductListViewModel(
        IProductService productService,
        INavigationService navigation)
    {
        _productService = productService;
        _navigation = navigation;
    }
}
```

### Shell Navigation with DI

```csharp
// AppShell.xaml - ContentTemplate cho lazy loading
<ShellContent Title="Products"
              ContentTemplate="{DataTemplate views:ProductListPage}" />
```

```csharp
// AppShell.xaml.cs - Manual resolution nếu cần
public partial class AppShell : Shell
{
    public AppShell(IServiceProvider serviceProvider)
    {
        InitializeComponent();
        
        // Optional: Resolve services if Shell needs them
        var authService = serviceProvider.GetRequiredService<IAuthService>();
    }
}
```

---

## Platform-Specific Services

```csharp
// Interface
public interface IDeviceService
{
    string GetDeviceId();
    string GetPlatformName();
}

// Shared partial class
public partial class DeviceService : IDeviceService
{
    public partial string GetDeviceId();
    
    public string GetPlatformName()
    {
        return DeviceInfo.Platform.ToString();
    }
}

// Platforms/Android/Services/DeviceService.cs
public partial class DeviceService
{
    public partial string GetDeviceId()
    {
        return Android.Provider.Settings.Secure.GetString(
            Android.App.Application.Context.ContentResolver,
            Android.Provider.Settings.Secure.AndroidId) ?? "unknown";
    }
}

// Platforms/iOS/Services/DeviceService.cs
public partial class DeviceService
{
    public partial string GetDeviceId()
    {
        return UIKit.UIDevice.CurrentDevice.IdentifierForVendor?.ToString() 
            ?? "unknown";
    }
}

// Registration
services.AddSingleton<IDeviceService, DeviceService>();
```

---

## HttpClient Configuration

```csharp
// Named HttpClient với retry policy
services.AddHttpClient<IApiClient, ApiClient>(client =>
{
    client.BaseAddress = new Uri("https://api.example.com/");
    client.DefaultRequestHeaders.Add("Accept", "application/json");
})
.ConfigurePrimaryHttpMessageHandler(() =>
{
#if ANDROID
    return new Xamarin.Android.Net.AndroidMessageHandler
    {
        ServerCertificateCustomValidationCallback = (_, _, _, _) => true // Dev only!
    };
#else
    return new HttpClientHandler();
#endif
})
.AddTransientHttpErrorPolicy(policy => 
    policy.WaitAndRetryAsync(3, retryAttempt => 
        TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))));
```

---

## Common Mistakes / Lỗi Thường Gặp

### 1. Singleton ViewModel (State Leaking)

```csharp
// ❌ SAI: Product từ lần navigate trước vẫn còn
services.AddSingleton<ProductDetailViewModel>();

// ✅ ĐÚNG: Fresh instance mỗi lần
services.AddTransient<ProductDetailViewModel>();
```

### 2. Missing View Registration

```csharp
// ❌ SAI: Shell không thể resolve page
// Exception: Unable to resolve ProductListPage

// ✅ ĐÚNG: Đăng ký cả View và ViewModel
services.AddTransient<ProductListViewModel>();
services.AddTransient<ProductListPage>();
```

### 3. Circular Dependencies

```csharp
// ❌ SAI: ServiceA → ServiceB → ServiceA
public class ServiceA
{
    public ServiceA(ServiceB b) { } // ServiceB cần ServiceA
}

// ✅ ĐÚNG: Dùng Lazy<T> hoặc refactor
public class ServiceA
{
    private readonly Lazy<ServiceB> _serviceB;
    
    public ServiceA(Lazy<ServiceB> serviceB)
    {
        _serviceB = serviceB;
    }
}
```

---

## Best Practices Checklist

- [ ] Views và ViewModels đều đăng ký là Transient
- [ ] Services giữ state toàn cục đăng ký là Singleton
- [ ] Stateless services đăng ký là Transient
- [ ] Inject interfaces, không inject concrete classes
- [ ] Sử dụng `IHttpClientFactory` cho HttpClient
- [ ] Platform-specific code dùng Partial Classes

---

## Resources

- [.NET MAUI Dependency Injection](https://learn.microsoft.com/en-us/dotnet/maui/fundamentals/dependency-injection)
- [MVVM Rules](./mvvm-rules.md)
- [Shell Navigation](./shell-nav.md)
