控制反转——Autofac框架

一、 组件注册方式

1. RegisterType(注册类型):“需要时请new一个”

builder.RegisterType<ExposureInfoViewModel>().AsSelf().SingleInstance();
  • 作用:用于将一个具体的类注册到容器中,告知容器“当需要某个服务时,可以创建这个类的实例来提供它”
  • 原理:告诉容器一个实现类型,当有组件请求该类型(或其实现的接口/基类)时,容器会new一个新的实例(根据生命周期配置,可能是新的,也可能是共享的)
  • 使用:配合As<>来指定其暴露的服务(接口)。

2. RegisterInstance(注册实例):“给你这个现成的,一直用它”

var mySingleton = new MySingletonClass();
builder.RegisterInstance(mySingleton).As<ISingletonService>();
  • 作用:用于将一个已经存在的、特定的对象实例注册到容器中。告诉容器:“不要自己创建新的,每次有人请求这个服务时,都返回 我给你的 这个唯一的、现成的实例”。
  • 原理:你将一个已经实例化好的对象交给容器。容器不会尝试创建它、也不会管理它的销毁。每次解析该服务,返回的都是同一个对象引用。
  • 特点:提供现有对象,容器不负责创建,只负责提供引用。
  • 场景
    • 注册已经在程序启动时创建好的单例对象。
    • 注册一些在容器之外创建和配置的对象(如第三方框架的入口点)。
    • 注册常量值或配置对象。
    • 你想完全控制某个对象的创建和初始化过程,然后将其提供给 DI 容器使用。

3. RegisterModule(注册模块):“把这些相关的注册打包一起执行”

public class DataAccessModule : Module
{
    public string ConnectionString { get; set; }

    protected override void Load(ContainerBuilder builder)
    {
        // 在这个模块内部,你可以像在外部一样使用 builder 进行注册
        builder.RegisterType<SqlUserRepository>()
               .As<IUserRepository>()
               .WithParameter(“connectionString”, ConnectionString) // 可以使用模块的属性来配置
               .InstancePerLifetimeScope();

        builder.RegisterType<SqlOrderRepository>()
               .As<IOrderRepository>()
               .WithParameter(“connectionString”, ConnectionString)
               .InstancePerLifetimeScope();

        // 也可以注册其他模块
        // builder.RegisterModule(new LoggingModule());
    }
}
//写法一:需要手动创建模块实例、可以在注册前对模块进行配置、适用于需要传递参数的模块
builder.RegisterModule(new DataAccessModule { ConnectionString = “MyConnString” });

//写法二:Autofac 会自动实例化模块、代码更简洁、要求模块有无参构造函数
builder.RegisterModule<DataAccessModule >()
  • 作用:它不是直接注册服务,而是注册一个“模块”。模块是一个包含了一组相关注册的的逻辑单元。它是一种组织和管理注册代码的方式,用于实现关注点分离和模块化设计。
  • 原理:创建一个继承自Autofac.Module的类,在其Load方法中编写一系列的注册语句。然后通过RegisterModule将这个类加入到容器构建器中,类内的所有注册都会被执行。
  • 特点
    • 组织代码:将属于同一功能块(如数据访问、日志、Web API 控制器)的注册封装在一起,让 Global.asaxProgram.cs 中的代码更简洁。
    • 可配置性:模块可以有自己的属性,允许在注册时进行配置(如上面的连接字符串)。
    • 可重用性:封装好的模块可以在不同的项目中重用。
    • 条件注册:可以在模块的 Load 方法中编写逻辑,实现有条件的动态注册。

二、 服务暴露方式

1. AsSelf()

  • 注册方式:将类本身作为服务注册,即类直接对应注册的服务。
  • 适用场景:适用于类未继承任何接口的情况,或需要明确指定服务类型为类本身。
builder.RegisterType<ExposureInfoViewModel>().AsSelf().SingleInstance();
- builder.RegisterType<ExposureInfoViewModel>():告诉 Autofac 容器注册 ExposureInfoViewModel 类型。这样,当你需要 ExposureInfoViewModel 的实例时,Autofac 能自动创建并注入它。
- .AsSelf():指定注册时以自身类型(ExposureInfoViewModel)来解析。也就是说,只有请求 ExposureInfoViewModel 时才会返回这个实例,而不是以接口或基类的方式。
- .SingleInstance():指定该类型在整个容器生命周期内只创建一个实例(单例模式)。所有请求都会获得同一个 ExposureInfoViewModel 实例。

2. As<接口>()

  • 注册方式:将类注册为指定接口的实现,即通过接口暴露服务。
  • 适用场景:当类继承了接口时使用,可实现接口与类的关联,便于后续通过接口统一调用实现类。
builder.RegisterType<McsfLoggerFactory>().As<ILoggerFactory>().SingleInstance();

- .As<ILoggerFactory>():注册时,声明当请求 ILoggerFactory 接口时,实际提供 McsfLoggerFactory 的实例。也就是说,依赖注入时如果有类需要 ILoggerFactory,Autofac 会注入 McsfLoggerFactory。

3. 联合使用

builder.RegisterType<MultiService>().As<IServiceA>().As<IServiceB>().SingleInstance();

 - .As<IServiceA>().As<IServiceB>():允许单个实现类同时作为多个接口的服务提供者,解析IServiceA或IServiceB时返回同一对象实例
 - 典型场景:
    - 日志服务同时实现IFileLogger和IConsoleLogger
    - 数据访问层同时支持IRepository和IQueryProvider接口

builder.RegisterType<MyService>().As<IMyService>().AsSelf();

- .As<IMyService>().AsSelf():既可通过IMyService解析,也可直接解析MyService类型
- 典型场景:
  - 需要直接访问实现类特有方法(非接口声明的方法)
  - 单元测试中需Mock具体类而非接口

三、 生命周期管理方式

1. SingleInstance(单例模式)

  • 在整个容器生命周期内只会创建一个实例,所有解析请求都返回同一个对象
  • 适用于无状态服务或全局共享资源(如配置类、日志服务)
  • 线程安全,适合多线程环境下的共享访问  
using Autofac;
using System;

public interface IService { Guid Id { get; } }
public class MyService : IService 
{
    public Guid Id { get; } = Guid.NewGuid();
}

class Program
{
    static void Main()
    {
        var builder = new ContainerBuilder();
        // 关键注册:配置单例生命周期
        builder.RegisterType<MyService>().As<IService>().SingleInstance();

        using (var container = builder.Build())
        {
            // 测试单例行为
            using (var scope1 = container.BeginLifetimeScope())
            {
                var service1 = scope1.Resolve<IService>();
                var service2 = scope1.Resolve<IService>();
                Console.WriteLine($"同作用域实例比对: {service1.Id == service2.Id}"); // True
            }

            // 新建作用域测试
            using (var scope2 = container.BeginLifetimeScope())
            {
                var service3 = scope2.Resolve<IService>();
                var service4 = scope2.Resolve<IService>();
                Console.WriteLine($"跨作用域实例比对: {service3.Id == service4.Id}"); // True
            }
        }
    }
}

2. InstancePerDependency(瞬时模式)

  • 每次解析请求都会创建一个新实例,是Autofac的默认行为
  • 适用于需要独立状态的短期对象(如请求处理器、临时计算服务)
  • 可能增加内存开销,频繁创建/销毁对象时需评估性能
using Autofac;
using System;

public interface ILogger { void Log(string message); }
public class ConsoleLogger : ILogger 
{
    private Guid _id = Guid.NewGuid();
    public void Log(string message) => 
        Console.WriteLine($"[实例ID:{_id}] {message}");
}

class Program
{
    static void Main()
    {
        var builder = new ContainerBuilder();
        // 关键注册:配置瞬时生命周期(默认行为,显式声明更清晰)
        builder.RegisterType<ConsoleLogger>().As<ILogger>().InstancePerDependency();

        using (var container = builder.Build())
        {
            // 同一作用域内解析两次
            using (var scope = container.BeginLifetimeScope())
            {
                var logger1 = scope.Resolve<ILogger>();//每次调用Resolve()都会生成全新实例,通过Guid验证实例唯一性
                var logger2 = scope.Resolve<ILogger>();
                
                logger1.Log("第一次调用"); // 输出不同实例ID
                logger2.Log("第二次调用");
            }
        }
    }
}

???builder.RegisterInstance(ConsoleLogger).As<ILogger>().InstancePerDependency(); 每次请求得到的是同一个实例吗

答:RegisterInstance和InstancePerDependency本身是矛盾且无效的,最终的行为将由容器决定,但通常会产生非预期的结果。

3. InstancePerLifetimeScope(作用域模式)

  • 在同一生命周期作用域内返回同一实例,不同作用域创建不同实例
  • 典型应用场景包括HTTP请求周期(如ASP.NET中每个请求一个作用域)
  • 需显式调用BeginLifetimeScope()创建作用域,避免内存泄漏

  image

using Autofac;
using System;

public interface IService { void Execute(); }
public class MyService : IService 
{
    public void Execute() => Console.WriteLine($"实例ID: {this.GetHashCode()}");
}

class Program
{
    static void Main()
    {
        var builder = new ContainerBuilder();
        // 关键注册:配置作用域生命周期
        builder.RegisterType<MyService>().As<IService>().InstancePerLifetimeScope();

        using (var container = builder.Build())
        {
            // 场景1:同一作用域内实例共享
            using (var scope1 = container.BeginLifetimeScope())
            {
                var s1 = scope1.Resolve<IService>();
                var s2 = scope1.Resolve<IService>();
                s1.Execute(); // 输出相同哈希值
                s2.Execute();
            }

            // 场景2:不同作用域创建新实例
            using (var scope2 = container.BeginLifetimeScope())
            {
                var s3 = scope2.Resolve<IService>();
                s3.Execute(); // 输出与scope1不同的哈希值
            }
        }
    }
}

 

posted @ 2025-08-28 10:20  LXLR  阅读(32)  评论(0)    收藏  举报