ZetCode

ASP.NET 依赖注入

最后修改于 2025 年 4 月 3 日

在本文中,我们探讨 ASP.NET 8 中的依赖注入 (DI)。DI 是一种设计模式,它实现控制反转来解决依赖关系。

ASP.NET Core 和 .NET 8 内置了对依赖注入的支持。这使得应用程序默认更加模块化、可测试和可维护。

基本定义

依赖注入是一种技术,其中对象从外部源接收其依赖项,而不是直接创建它们。这促进了组件之间的松耦合。

在 ASP.NET 中,DI 容器管理服务的生命周期,并将它们注入到需要它们的类中。服务在应用程序启动时注册。

内置的 DI 容器支持三种主要的生命周期选项:瞬态 (Transient)、作用域 (Scoped) 和单例 (Singleton)。每种都有不同的实例创建行为。

构造函数注入是 ASP.NET 中的首选方法,其中依赖项通过类构造函数提供。这使得依赖项更加明确。

ASP.NET 依赖注入示例

以下示例演示了一个使用 DI 的完整 ASP.NET 应用程序。

Program.cs
var builder = WebApplication.CreateBuilder(args);

// Register services with different lifetimes
builder.Services.AddTransient<ITransientService, TransientService>();
builder.Services.AddScoped<IScopedService, ScopedService>();
builder.Services.AddSingleton<ISingletonService, SingletonService>();

// Register a custom repository
builder.Services.AddScoped<IProductRepository, ProductRepository>();

var app = builder.Build();

app.MapGet("/", (ITransientService transient1, ITransientService transient2,
    IScopedService scoped1, IScopedService scoped2,
    ISingletonService singleton1, ISingletonService singleton2) =>
{
    return Results.Ok(new
    {
        Transient1 = transient1.GetId(),
        Transient2 = transient2.GetId(),
        Scoped1 = scoped1.GetId(),
        Scoped2 = scoped2.GetId(),
        Singleton1 = singleton1.GetId(),
        Singleton2 = singleton2.GetId()
    });
});

app.MapControllers();
app.Run();

这会设置一个具有各种服务注册的 ASP.NET 应用程序。端点通过显示实例 ID 来演示不同的服务生命周期。

Services/ProductRepository.cs
public interface IProductRepository
{
    IEnumerable<Product> GetAll();
    Product? GetById(int id);
}

public class ProductRepository : IProductRepository
{
    private readonly List<Product> _products = new()
    {
        new Product(1, "Laptop", 999.99m),
        new Product(2, "Mouse", 19.99m),
        new Product(3, "Keyboard", 49.99m)
    };

    public IEnumerable<Product> GetAll() => _products;

    public Product? GetById(int id) => 
        _products.FirstOrDefault(p => p.Id == id);
}

此存储库提供产品的访问数据。接口定义了契约,而具体类实现了实际的数据操作。

Controllers/ProductsController.cs
using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
    private readonly IProductRepository _repository;
    private readonly ILogger<ProductsController> _logger;

    public ProductsController(
        IProductRepository repository,
        ILogger<ProductsController> logger)
    {
        _repository = repository;
        _logger = logger;
    }

    [HttpGet]
    public IActionResult GetAll()
    {
        _logger.LogInformation("Getting all products");
        return Ok(_repository.GetAll());
    }

    [HttpGet("{id}")]
    public IActionResult GetById(int id)
    {
        var product = _repository.GetById(id);
        if (product == null)
        {
            _logger.LogWarning("Product {Id} not found", id);
            return NotFound();
        }
        return Ok(product);
    }
}

public record Product(int Id, string Name, decimal Price);

控制器演示了构造函数注入,接收自定义存储库和内置的 ILogger 服务。存储库用于获取产品数据。

该示例展示了 ASP.NET 在创建控制器实例时如何自动解析和注入依赖项。记录器在没有显式注册的情况下被注入,因为它属于框架的一部分。

此实现遵循依赖倒置原则,依赖于抽象(接口)而不是具体实现。这使得代码更易于测试和维护。

来源

Microsoft ASP.NET 依赖注入文档

在本文中,我们探讨了 ASP.NET 8 中的依赖注入。这个强大的功能有助于创建松耦合、可维护的应用程序。

作者

我叫 Jan Bodnar,是一位热情的程序员,拥有丰富的编程经验。我自 2007 年以来一直在撰写编程文章。至今,我已撰写了 1,400 多篇文章和 8 本电子书。我在教授编程方面拥有十多年的经验。

列出所有 ASP.NET 教程