如何基于Autofac实现多Tree实例(各实例子服务注册不同)?
用Autofac实现多实例、可配置的Tree组件方案
这是个非常典型的「带专属配置的实例组」需求,不用搞那种怪异的非容器隔离写法,Autofac有两种非常优雅的解决思路,我给你拆解清楚:
方案一:模块+命名服务(适合有共享服务的场景)
这种方式通过命名标识区分不同的Tree实例,同时用Autofac模块来封装每个实例的配置逻辑,代码复用性高,还能在根容器统一管理所有服务。
步骤1:封装Tree配置模块
先写一个通用的TreeModule,它接受实例名称和内部服务的配置委托,负责注册该实例专属的内部服务和带命名的外部服务:
public class TreeModule : Module { private readonly string _treeInstanceName; private readonly Action<ContainerBuilder> _internalServicesConfig; public TreeModule(string treeInstanceName, Action<ContainerBuilder> internalServicesConfig) { _treeInstanceName = treeInstanceName; _internalServicesConfig = internalServicesConfig; } protected override void Load(ContainerBuilder builder) { // 注册当前Tree实例专属的10个内部服务 _internalServicesConfig(builder); // 给外部服务(TreeCommand/TreeQuery)加上命名标识,关联当前实例的内部服务 builder.RegisterType<TreeCommand>() .Named<ITreeCommand>(_treeInstanceName) .InstancePerDependency(); // 可根据需求调整生命周期 builder.RegisterType<TreeQuery>() .Named<ITreeQuery>(_treeInstanceName) .InstancePerDependency(); } }
步骤2:注册不同的Tree实例
在容器构建时,给每个Tree实例(Student/Teacher/Company)单独注册模块,传入对应的内部服务实现:
var builder = new ContainerBuilder(); // 注册StudentTree builder.RegisterModule(new TreeModule("Student", b => { b.RegisterType<StudentInternalService1>().As<ITreeInternalService1>(); b.RegisterType<StudentInternalService2>().As<ITreeInternalService2>(); // ... 剩下8个内部服务的专属实现 })); // 注册TeacherTree builder.RegisterModule(new TreeModule("Teacher", b => { b.RegisterType<TeacherInternalService1>().As<ITreeInternalService1>(); b.RegisterType<TeacherInternalService2>().As<ITreeInternalService2>(); // ... 剩下8个内部服务的专属实现 })); // 注册CompanyTree同理... var container = builder.Build();
步骤3:使用指定的Tree实例
通过ResolveNamed方法,根据名称获取对应实例的外部服务:
// 获取StudentTree的命令和查询服务 var studentCommand = container.ResolveNamed<ITreeCommand>("Student"); var studentQuery = container.ResolveNamed<ITreeQuery>("Student"); // 获取TeacherTree的服务同理 var teacherCommand = container.ResolveNamed<ITreeCommand>("Teacher");
方案二:子容器(适合完全隔离的场景)
如果你的每个Tree实例需要完全隔离,不想用命名来区分,Autofac的嵌套子容器是更干净的选择。子容器会继承根容器的服务,但可以覆盖或新增自己的服务,完美实现实例级隔离。
步骤1:构建根容器(可选)
如果有全局共享的服务,先注册到根容器;如果没有,直接创建根容器即可:
var rootBuilder = new ContainerBuilder(); // 注册全局共享服务(比如日志、数据库连接等) // rootBuilder.RegisterType<GlobalLogger>().As<ILogger>(); var rootContainer = rootBuilder.Build();
步骤2:为每个Tree实例创建子容器
每个子容器单独注册专属的内部服务和外部服务:
// 创建StudentTree的子容器 var studentTreeBuilder = new ContainerBuilder(); studentTreeBuilder.RegisterType<StudentInternalService1>().As<ITreeInternalService1>(); studentTreeBuilder.RegisterType<StudentInternalService2>().As<ITreeInternalService2>(); // ... 注册所有内部服务 studentTreeBuilder.RegisterType<TreeCommand>().As<ITreeCommand>(); studentTreeBuilder.RegisterType<TreeQuery>().As<ITreeQuery>(); var studentTreeContainer = rootContainer.CreateChildContainer(studentTreeBuilder); // 创建TeacherTree的子容器 var teacherTreeBuilder = new ContainerBuilder(); teacherTreeBuilder.RegisterType<TeacherInternalService1>().As<ITreeInternalService1>(); teacherTreeBuilder.RegisterType<TeacherInternalService2>().As<ITreeInternalService2>(); // ... 注册所有内部服务 teacherTreeBuilder.RegisterType<TreeCommand>().As<ITreeCommand>(); teacherTreeBuilder.RegisterType<TreeQuery>().As<ITreeQuery>(); var teacherTreeContainer = rootContainer.CreateChildContainer(teacherTreeBuilder);
步骤3:使用子容器中的服务
直接从对应子容器解析服务即可,完全不需要命名:
var studentCommand = studentTreeContainer.Resolve<ITreeCommand>(); var teacherQuery = teacherTreeContainer.Resolve<ITreeQuery>(); // 记得在不需要子容器时释放资源 // studentTreeContainer.Dispose();
方案选择建议
- 如果Tree实例之间有共享的内部服务,或者希望在根容器统一管理所有服务,选模块+命名服务;
- 如果每个Tree实例完全独立,不想处理命名冲突,选子容器,隔离性更强,代码更直观。
额外优化建议
可以封装一个ITreeFactory来统一创建和管理Tree实例,避免在启动代码里写大量重复的注册逻辑,让代码更整洁。
内容的提问来源于stack exchange,提问作者Oguz Karadenizli
相关产品推荐
相关产品推荐

