云计算百科
云计算领域专业知识百科平台

C# | Autofac 新手指南:从零理解依赖注入与组件装配

在这里插入图片描述

C# Autofac 新手指南:从零理解依赖注入与组件装配

文章目录

  • C# Autofac 新手指南:从零理解依赖注入与组件装配
    • 1 什么是依赖注入与 Autofac
      • 1.1 概念讲解:搞懂三个核心术语
      • 1.2 主流 IoC 容器对比与应用场景
      • 1.3 C# 与依赖注入的发展历程
    • 2 Autofac/DI 代码与传统代码的对比
      • 2.1 代码特点对比
        • 传统代码(硬编码直接依赖)
        • Autofac / DI 代码(解耦与面向接口)
      • 2.2 Autofac 代码风格与设计优势
      • 2.3 帮助理解依赖注入与 Autofac 的方法
    • 3 使用 Autofac 的意义与架构定位
      • 3.1 适用场景与不适用场景
        • 适用场景
        • 不适用场景
      • 3.2 Autofac 在 DDD 系统架构中的位置
      • 3.3 实际案例演示:解耦前后对比
        • 场景:系统升级,需要将订单储存方式从 SQL Server 升级为 Redis 缓存。
    • 4 手把手体验 Autofac
      • 4.1 第一行 Autofac 代码( Console 控制台极简体验)
      • 4.2 语法与核心 API 详解
        • 4.2.1 组件注册方式 (Registration)
          • 1. 类型注册 (RegisterType)
          • 2. 程序集扫描注册 (RegisterAssemblyTypes) —— **Autofac 的杀手级特性**
          • 3. 模块化注册 (Module)
        • 4.2.2 生命周期管理 (Lifetime Management)
          • 代码应用示例:
    • 5 总结

1 什么是依赖注入与 Autofac

1.1 概念讲解:搞懂三个核心术语

在进入 Autofac 之前,很多初学者容易被 IoC、DIP 和 DI 这三个英文缩写绕晕。用现实生活中的例子可以轻松理清它们的关系:

在这里插入图片描述

  • DIP(Dependency Inversion Principle,依赖倒置原则):

  • 含义: 一项设计原则。高层模块不应该依赖底层模块,两者都应该依赖于抽象(接口或抽象类);抽象不应该依赖细节,细节应该依赖抽象。

  • 生活比喻: 墙上的插座(接口)不关心接进来的是电风扇还是吹风机(具体实现),只要符合国家插头标准,就能供电。

  • IoC(Inversion of Control,控制反转):

  • 含义: 一种设计思想。把对象的创建、组装和生命周期控制权,从代码本身“反转”交给外部容器或框架。

  • 生活比喻: 以前你想吃汉堡,必须自己准备面粉、牛肉并亲自下厨(主动创建依赖);现在你去麦当劳,点单后由厨师做出汉堡递给你(控制权反转)。

  • DI(Dependency Injection,依赖注入):

  • 含义: IoC 思想的具体实现技术。在运行期间,由外部容器将被依赖的对象通过构造函数、属性或方法“注入”给使用它的对象。

  • Autofac 的角色: Autofac 就是一个功能极其强大、高性能的 .NET IoC 容器框架,负责帮你在应用中自动化完成依赖注入的繁琐工作。


1.2 主流 IoC 容器对比与应用场景

在 .NET 生态中,存在多种 IoC 容器。了解它们的差异有助于在项目中做出正确的技术选型:

容器名称核心优势潜在短板最佳应用场景
Microsoft.Extensions.DependencyInjection (原生) 零额外包引入,极轻量,开箱即用 功能较为基础,缺乏自动程序集扫描、属性注入和复杂的生命周期控制 小型项目、标准微服务、逻辑简单的 API 服务
Autofac 功能极为全面,支持程序集批量注册、模块化配置(Module)、属性注入、拦截器(AOP) 相比原生注入有少量学习成本 中大型项目、复杂企业级系统、采用 DDD 架构的应用
Castle Windsor 历史悠久,扩展性极强 相对偏重,近几年更新频率较低 维护较早期的传统企业级 .NET Framework 项目

1.3 C# 与依赖注入的发展历程

在这里插入图片描述

  • 硬编码时代: 在早期 C# 开发中,大部分对象都在类内部直接 new 出来,导致修改一个底层实现必须改动几十处上层代码,代码几乎无法进行单元测试。
  • 原生 DI 时代: .NET Core 推出后,微软将依赖注入作为框架的一等公民内嵌在系统底层,绝大多数开发者开始全面接受 DI 模式。
  • Autofac 增强时代: 随着业务复杂度提升,原生 DI 的功能无法满足大面积自动化注册、动态代理拦截等需求。Autofac 凭借与微软原生 DI 无缝集成的能力,成为了增强型第三方容器的首选。

  • 2 Autofac/DI 代码与传统代码的对比

    2.1 代码特点对比

    假设需要实现一个订单结算服务,它依赖于数据库读取组件(OrderRepository)。

    在这里插入图片描述

    传统代码(硬编码直接依赖)

    // 数据库读取类
    public class OrderRepository
    {
    public string GetOrderData(int orderId) => $"Order #{orderId} data from SQL Server";
    }

    // 订单服务类
    public class OrderService
    {
    private readonly OrderRepository _repository;

    public OrderService()
    {
    // 严重问题:OrderService 强绑定了具体的 OrderRepository
    // 如果想换成 MongoDbRepository,必须修改此类源代码
    _repository = new OrderRepository();
    }

    public void ProcessOrder(int orderId)
    {
    var data = _repository.GetOrderData(orderId);
    Console.WriteLine($"Processing: {data}");
    }
    }

    Autofac / DI 代码(解耦与面向接口)

    // 1. 定义抽象接口
    public interface IOrderRepository
    {
    string GetOrderData(int orderId);
    }

    // 2. 实现接口
    public class SqlOrderRepository : IOrderRepository
    {
    public string GetOrderData(int orderId) => $"Order #{orderId} data from SQL Server";
    }

    // 3. 订单服务依赖抽象接口,而非具体实现
    public class OrderService
    {
    private readonly IOrderRepository _repository;

    // 构造函数注入:由 Autofac 在运行时自动传人具体的实现对象
    public OrderService(IOrderRepository repository)
    {
    _repository = repository;
    }

    public void ProcessOrder(int orderId)
    {
    var data = _repository.GetOrderData(orderId);
    Console.WriteLine($"Processing: {data}");
    }
    }


    2.2 Autofac 代码风格与设计优势

  • Fluent API 语法链: Autofac 提供了极具可读性的链式调用 API,配置过程就像写自然语言一样清晰。
  • 模块化设计(Autofac Module): 允许将复杂的注册逻辑按照业务拆分到独立的模块类中,避免 Program.cs 变得臃肿堪比“万字长文”。
  • 自动装配(Auto-wiring): 在解析对象时,Autofac 会自动检查其构造函数的入参类型,层层递归地递归解析并实例化所有依赖项,无需手动声明组装顺序。

  • 2.3 帮助理解依赖注入与 Autofac 的方法

    将构建软件系统想象成 “台式电脑组装”:

    • 传统代码: 就像主板上直接把 CPU、内存条焊死。虽然能用,但一旦内存不够用,整台电脑只能报废。
    • 面向接口(DIP): 主板定义了 DDR5 内存插槽标准,无论谁生产的内存条,符合标准就能插上。
    • Autofac(装配工厂): 你是客户,写了一份配置单(注册组件),Autofac 是自动化装配车间。它看到你需要一台电脑,就会去仓库拿符合接口标准的内存条、CPU 和显卡,组装好后直接整机交付给你使用。

    3 使用 Autofac 的意义与架构定位

    3.1 适用场景与不适用场景

    在这里插入图片描述

    适用场景
    • 大型/多模块系统: 项目中有成百上千个 Service 和 Repository,需要通过程序集自动批量注册。
    • 需要 AOP 动态代理: 需要无侵入地在方法执行前后加上日志记录、性能统计、事务控制或缓存逻辑。
    • 复杂领域驱动设计(DDD): 层次清晰、依赖关系错综复杂,需要灵活的生命周期隔离和模块装配。
    不适用场景
    • 一次性命令行小工具: 代码总量只有几百行,不需要后期维护,引入 DI 框架反而徒增代码复杂度。
    • 性能极度敏感的核心计算算法内部: 如果某个极其底层的紧密循环每秒需要执行几百万次,通过 DI 容器动态解析会带来毫秒级/微秒级的额外反射耗时,此时直接实例化或静态方法更为适宜。

    3.2 Autofac 在 DDD 系统架构中的位置

    在标准的 DDD(领域驱动设计)分层架构中,底层基础设施层(Infrastructure)实现了领域层(Domain)和应用层(Application)定义的接口。

    为了避免上层代码直接引用基础设施层的具体类,Autofac 存在于系统的“组合根”(Composition Root)中(通常即最外层的程序入口点,如 API 层的 Startup.cs 或 Program.cs)。

    #mermaid-svg-97rOkttkaj1mCQbE{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-97rOkttkaj1mCQbE .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-97rOkttkaj1mCQbE .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-97rOkttkaj1mCQbE .error-icon{fill:#552222;}#mermaid-svg-97rOkttkaj1mCQbE .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-97rOkttkaj1mCQbE .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-97rOkttkaj1mCQbE .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-97rOkttkaj1mCQbE .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-97rOkttkaj1mCQbE .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-97rOkttkaj1mCQbE .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-97rOkttkaj1mCQbE .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-97rOkttkaj1mCQbE .marker{fill:#333333;stroke:#333333;}#mermaid-svg-97rOkttkaj1mCQbE .marker.cross{stroke:#333333;}#mermaid-svg-97rOkttkaj1mCQbE svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-97rOkttkaj1mCQbE p{margin:0;}#mermaid-svg-97rOkttkaj1mCQbE .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-97rOkttkaj1mCQbE .cluster-label text{fill:#333;}#mermaid-svg-97rOkttkaj1mCQbE .cluster-label span{color:#333;}#mermaid-svg-97rOkttkaj1mCQbE .cluster-label span p{background-color:transparent;}#mermaid-svg-97rOkttkaj1mCQbE .label text,#mermaid-svg-97rOkttkaj1mCQbE span{fill:#333;color:#333;}#mermaid-svg-97rOkttkaj1mCQbE .node rect,#mermaid-svg-97rOkttkaj1mCQbE .node circle,#mermaid-svg-97rOkttkaj1mCQbE .node ellipse,#mermaid-svg-97rOkttkaj1mCQbE .node polygon,#mermaid-svg-97rOkttkaj1mCQbE .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-97rOkttkaj1mCQbE .rough-node .label text,#mermaid-svg-97rOkttkaj1mCQbE .node .label text,#mermaid-svg-97rOkttkaj1mCQbE .image-shape .label,#mermaid-svg-97rOkttkaj1mCQbE .icon-shape .label{text-anchor:middle;}#mermaid-svg-97rOkttkaj1mCQbE .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-97rOkttkaj1mCQbE .rough-node .label,#mermaid-svg-97rOkttkaj1mCQbE .node .label,#mermaid-svg-97rOkttkaj1mCQbE .image-shape .label,#mermaid-svg-97rOkttkaj1mCQbE .icon-shape .label{text-align:center;}#mermaid-svg-97rOkttkaj1mCQbE .node.clickable{cursor:pointer;}#mermaid-svg-97rOkttkaj1mCQbE .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-97rOkttkaj1mCQbE .arrowheadPath{fill:#333333;}#mermaid-svg-97rOkttkaj1mCQbE .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-97rOkttkaj1mCQbE .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-97rOkttkaj1mCQbE .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-97rOkttkaj1mCQbE .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-97rOkttkaj1mCQbE .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-97rOkttkaj1mCQbE .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-97rOkttkaj1mCQbE .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-97rOkttkaj1mCQbE .cluster text{fill:#333;}#mermaid-svg-97rOkttkaj1mCQbE .cluster span{color:#333;}#mermaid-svg-97rOkttkaj1mCQbE div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-97rOkttkaj1mCQbE .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-97rOkttkaj1mCQbE rect.text{fill:none;stroke-width:0;}#mermaid-svg-97rOkttkaj1mCQbE .icon-shape,#mermaid-svg-97rOkttkaj1mCQbE .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-97rOkttkaj1mCQbE .icon-shape p,#mermaid-svg-97rOkttkaj1mCQbE .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-97rOkttkaj1mCQbE .icon-shape .label rect,#mermaid-svg-97rOkttkaj1mCQbE .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-97rOkttkaj1mCQbE .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-97rOkttkaj1mCQbE .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-97rOkttkaj1mCQbE :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    组合根 (Composition Root) – 程序入口点

    基础设施层 (Infrastructure)

    领域层 (Domain)

    应用层 (Application)

    UI / API 层 (Presentation)

    实现

    实现

    1. 自动扫描绑定

    1. 自动扫描绑定

    2. 解析并注入到

    2. 解析并注入到

    Controllers / Endpoints

    App Services / Command Handlers

    Domain Services

    IRepository / IExternalService (抽象接口)

    EfOrderRepository

    RabbitMqPublisher

    Autofac ContainerBuilder(组装与装配中心)

    关键点: Autofac 是连接抽象接口和具体实现的“胶水”,它站在最外层统一感知所有的具体类型,并将具体实现注入到上层架构中。


    3.3 实际案例演示:解耦前后对比

    场景:系统升级,需要将订单储存方式从 SQL Server 升级为 Redis 缓存。
    • 没有 Autofac: 需要去修改 OrderService 中的 new SqlOrderRepository() 逻辑;如果有 50 个 Service 都用到了它,就需要修改 50 处代码。
    • 使用 Autofac: OrderService 的代码一行都不用变,只需要修改 Autofac 的集中注册配置:

    // 原注册:绑定为 SQL 实现
    // builder.RegisterType<SqlOrderRepository>().As<IOrderRepository>();

    // 现注册:改绑为 Redis 实现
    builder.RegisterType<RedisOrderRepository>().As<IOrderRepository>();

    整个系统的全部业务代码自动切换为最新的存储逻辑,改动范围被严格限定在系统配置入口。


    4 手把手体验 Autofac

    4.1 第一行 Autofac 代码( Console 控制台极简体验)

    可以通过 NuGet 安装包:Install-Package Autofac。

    using Autofac;
    using System;

    namespace AutofacDemo
    {
    // 1. 定义接口和类
    public interface IGreetService
    {
    void SayHello(string name);
    }

    public class ConsoleGreetService : IGreetService
    {
    public void SayHello(string name) => Console.WriteLine($"Hello, {name}!");
    }

    class Program
    {
    static void Main(string[] args)
    {
    // 2. 创建容器构建器 (ContainerBuilder)
    var builder = new ContainerBuilder();

    // 3. 注册组件:将 ConsoleGreetService 注册为 IGreetService 的实现
    builder.RegisterType<ConsoleGreetService>().As<IGreetService>();

    // 4. 构建容器 (IContainer)
    using (var container = builder.Build())
    {
    // 5. 从容器中解析组件 (Resolve)
    var greetService = container.Resolve<IGreetService>();

    // 6. 使用组件
    greetService.SayHello("Autofac 新手");
    } // 离开 using 作用域,容器及自动管理的资源会被统一释放
    }
    }
    }


    4.2 语法与核心 API 详解

    4.2.1 组件注册方式 (Registration)

    Autofac 提供了极具弹性的注册方式,最常用的有以下三种: 在这里插入图片描述

    1. 类型注册 (RegisterType)

    最基础的针对单个类的注册。

    builder.RegisterType<SqlOrderRepository>().As<IOrderRepository>();

    2. 程序集扫描注册 (RegisterAssemblyTypes) —— Autofac 的杀手级特性

    无需逐个手动注册,自动扫描指定程序集(Assembly)下所有的类,按照规则批量注册。

    var repositoryAssembly = System.Reflection.Assembly.GetExecutingAssembly();

    builder.RegisterAssemblyTypes(repositoryAssembly)
    .Where(t => t.Name.EndsWith("Repository")) // 类名以 Repository 结尾
    .AsImplementedInterfaces(); // 自动绑定它实现的所有接口

    3. 模块化注册 (Module)

    将逻辑相近的组件配置打包封装到一个单独的类中。

    // 定义模块
    public class DatabaseModule : Autofac.Module
    {
    protected override void Load(ContainerBuilder builder)
    {
    builder.RegisterType<SqlOrderRepository>().As<IOrderRepository>();
    builder.RegisterType<DbContext>().InstancePerLifetimeScope();
    }
    }

    // 主入口中直接加载模块
    builder.RegisterModule<DatabaseModule>();


    4.2.2 生命周期管理 (Lifetime Management)

    当向 Autofac 请求某个对象时,Autofac 是重新 new 一个,还是复用已有的?这就是生命周期决定的。

    生命周期 API官方名称行为说明适用场景
    InstancePerDependency() 瞬态 (Transient) 默认策略。每次调用 Resolve 或注入时,都创建一个全新的实例 无状态的轻量级 Service、工具类
    SingleInstance() 单例 (Singleton) 整个应用程序生命周期内只创建一次实例,后续解析全部复用该对象 全局配置类、内存缓存管理器
    InstancePerLifetimeScope() 作用域 (Scoped) 在同一个 LifetimeScope(如一次 HTTP 请求)内共享同一个实例 数据库上下文(如 Entity Framework DbContext)
    代码应用示例:

    // 单例模式:全局唯一
    builder.RegisterType<CacheManager>().As<ICacheManager>().SingleInstance();

    // Scoped 模式:同一作用域/HTTP请求内唯一
    builder.RegisterType<MyDbContext>().InstancePerLifetimeScope();


    5 总结

    学习 Autofac 的核心价值不在于记忆 API,而在于理解并践行“解耦”的设计哲学:

  • 高内聚低耦合: 让类只专注于自己的核心业务逻辑,不再操心依赖项如何创建与组装。
  • 面向接口编程: 通过接口定义行为,实现类由 Autofac 动态组装,大幅提升系统的灵活性与可维护性。
  • 拥抱自动化: 充分利用 Autofac 的程序集扫描和模块化配置(Module),减少手工组装代码的冗余与差错。
  • 迈出第一步后,你可以在下一个 ASP.NET Core 项目中,尝试使用 Autofac 替换原生的 DI 容器,体会模块化和程序集扫描带来的高效开发体验。

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » C# | Autofac 新手指南:从零理解依赖注入与组件装配
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!