IoC之AutoFac(三)——生命周期
阅读目录
一、Autofac中的生命周期相关概念
服务的生命周期:是服务实例在您的应用程序中生存的时间 ,即从原始实例化到释放期间。例如,如果你“新建”了一个实现了IDisposable
的对象,然后再调用Dispose()
,那么这个对象的生命周期就是从你实例化的时候开始,被释放时结束(或者垃圾回收,如果你没有主动处置它)。
服务范围:应用程序中可以与其他使用该服务的组件共享该服务的区域。例如,在你的应用程序中你可能有一个全局静态的单例 - 全局对象实例的“范围”将是整个应用程序。另一方面,您可以在使用全局单例的for循环中创建局部变量 - 局部变量的范围比全局范围小得多。
Autofac
中的生命周期概念:结合了这两个概念。生命周期的范围等同于您的应用程序中的一个工作单元。在解析服务问题时,Autofac
跟踪已解析的一次性(IDisposable
)组件,在工作单元结束时,您将释放关联的生命周期范围(scope),Autofac
将自动清理/处理已解析的服务。
生命周期管理的两件重要的事情就是共享和清理。
我们来看一个Web应用程序作为更具体的例子来说明生命周期范围的使用。 假设你有以下情况:
你有一个全局的单例日志记录服务。
两个并发请求进入Web应用程序。
每个请求都是一个逻辑的“工作单元”,每个请求都需要自己的订单处理服务。
每个订单处理服务都需要将日信息记录到日志服务中。
在这种情况下,您将拥有包含单例记录服务的根生存期范围,并且每个请求都有一个子生命周期范围,每个范围都有自己的订单处理服务:
+---------------------------------------------------+
| Autofac Container |
| Root Lifetime Scope |
| |
| Logging Service |
| ( 在所有请求中共享 ) |
| |
| +----------------------+ +----------------------+ |
| | First Request Scope | | Second Request Scope | |
| | | | | |
| | Order Processor | | Order Processor | |
| +----------------------+ +----------------------+ |
+---------------------------------------------------+
When each request ends, the request lifetime scope ends and the respective order processor gets disposed. The logging service, as a singleton, stays alive for sharing by future requests.
当每个请求结束时,请求生命周期范围(scope)被处理,相应的订单处理服务被销毁。 日志记录服务作为一个单例对象,在将来的请求中保持共享。
二、创建一个新的生命周期范围
您可以通过在任何现有生命周期作用域上从根容器开始调用BeginLifetimeScope()
方法来创建生命周期作用域。生命周期作用域是可销毁的,他们跟踪组件的处置,所以确保你总是调用“Dispose()”
或者把它们包装在“using”
语句中。
1 using(var scope = container.BeginLifetimeScope()) 2 { 3 //从作为根容器子项的作用域来解析服务 4 var service = scope.Resolve<IService>(); 5 6 //您也可以创建嵌套的作用域... 7 using(var unitOfWorkScope = scope.BeginLifetimeScope()) 8 { 9 var anotherService = unitOfWorkScope.Resolve<IOther>(); 10 } 11 }
三、实例周期范围
3.1 每个依赖一个实例(InstancePerDependency)
使用这个选项,每次请求服务都会返回一个新实例,这是默认选项。
var builder = new ContainerBuilder(); builder.RegisterType<Worker>(); builder.RegisterType<Worker>().InstancePerDependency();
下面的代码,每次循环都生成一个新的实例,一共生成 100 个实例。
1 using(var scope = container.BeginLifetimeScope()) 2 { 3 for(var i = 0; i < 100; i++) 4 { 5 //每次解析都获取一个新实例 6 var w = scope.Resolve<Worker>(); 7 w.DoWork(); 8 } 9 }
3.2 单个实例(SingleInstance)
使用这个选项,在根范围或嵌套范围中请求服务,都返回同一个的实例。使用 SingleInstance() 指定。
var builder = new ContainerBuilder(); builder.RegisterType<Worker>().SingleInstance();
下面的代码,w1 和 w2 始终是同一个对象,100 次循环只有一个 Worker 类的实例。
using(var scope1 = container.BeginLifetimeScope()) { for(var i = 0; i < 100; i++) { var w1 = scope1.Resolve<Worker>(); using(var scope2 = scope1.BeginLifetimeScope()) { var w2 = scope2.Resolve<Worker>(); } } }
3.3 每个生命周期范围一个实例 (InstancePerLifetimeScope)
使用这个选项,在特定的 ILifetimeScope 中请求服务,只返回一个实例。下面的代码中,scope1 中的 100 次 w1 是同一个对象,scope2 中的 100 次 w2 是同一个对象,但是 w1 和 w2 不是同一个对象。
1 var builder = new ContainerBuilder(); 2 builder.RegisterType<Worker>().InstancePerLifetimeScope(); 3 using(var scope1 = container.BeginLifetimeScope()) 4 { 5 for(var i = 0; i < 100; i++) 6 { 7 var w1 = scope1.Resolve<Worker>(); 8 } 9 } 10 11 using(var scope2 = container.BeginLifetimeScope()) 12 { 13 for(var i = 0; i < 100; i++) 14 { 15 var w2 = scope2.Resolve<Worker>(); 16 } 17 }
3.4 每个匹配的生命周期范围一个实例(InstancePerMatchingLifetimeScope)
类似于上面【每个生命周期范围一个实例】,但可以提供更多控制。使用此选项,允许为 ILifetimeScope 对象提供“标记”。在标记匹配的范围中只有一个实例。使用 InstancePerMatchingLifetimeScope() 方法指定。
var builder = new ContainerBuilder(); builder.RegisterType<Worker>().InstancePerMatchingLifetimeScope("myscope");
下面的代码中,w1 和 w2 相同,w3 和 w4 相同,但 w1 和 w3 不同。
1 using(var scope1 = container.BeginLifetimeScope("myscope")) 2 { 3 for(var i = 0; i < 100; i++) 4 { 5 var w1 = scope1.Resolve<Worker>(); 6 using(var scope2 = scope1.BeginLifetimeScope()) 7 { 8 var w2 = scope2.Resolve<Worker>(); 9 } 10 } 11 } 12 13 using(var scope3 = container.BeginLifetimeScope("myscope")) 14 { 15 for(var i = 0; i < 100; i++) 16 { 17 var w3 = scope3.Resolve<Worker>(); 18 using(var scope4 = scope1.BeginLifetimeScope()) 19 { 20 var w4 = scope4.Resolve<Worker>(); 21 } 22 } 23 }
3.5 每个请求一个实例( InstancePerRequest)
有些应用程序天然具有【请求】语义,例如 ASP.NET MVC 或 WebForm 应用程序。【每个请求一个实例】在【每个匹配的生命周期范围一个实例】基础上,通过提供范围标记,注册函数和常见类型集成实现。本质上是【每个匹配的生命周期范围一个实例】。
var builder = new ContainerBuilder(); builder.RegisterType<Worker>().InstancePerRequest();
ASP.NET Core 使用【每个生命周期范围一个实例】,而不是【每个请求一个实例】。
3.6 每个 Owned 一个实例 ( InstancePerOwned)
Owned<T> 隐式关联类型创建嵌套的生命周期范围。使用 instance-per-owned 注册,可将依赖限定在 owned 实例中。
var builder = new ContainerBuilder(); builder.RegisterType<MessageHandler>(); builder.RegisterType<ServiceForHandler>().InstancePerOwned<MessageHandler>();
本例中 ServiceForHandler 服务会限制在 MessageHandler 实例范围内。
using(var scope = container.BeginLifetimeScope()) { // MessageHandler 和附属的 ServiceForHandler // 在 scope 下面的一个微型的 lifetime scope 中。 // 解析 Owned<T> 需要程序员负责执行清理工作。 var h1 = scope.Resolve<Owned<MessageHandler>>(); h1.Dispose(); }
3.7 线程范围通过
InstancePerLifetimeScope,每个线程建立自己的LifetimeScope
var builder = new ContainerBuilder(); builder.RegisterType<Service>() .InstancePerLifetimeScope(); var container = builder.Build();
然后让每个创建自己的 lifetime scope
void ThreadStart() { using (var threadLifetime = container.BeginLifetimeScope()) { var thisThreadsInstance = threadLifetime.Resolve<MyThreadScopedComponent>();
}
}
重要:在多线程场景下,要小心不要将父范围清理掉。否则,派生线程中的子范围将无法解析服务。
每个线程都将有自己的 MyThreadScopedComponent 实例,本质上是生命周期范围内的单例。范围内的实例不会提供到外部,因此很容易保持线程间的组件隔离。
通过添加 ILifetimeScope 参数,可将父范围注入到生成线程的代码中,Autofac 会将当前范围自动注入,接下来可以使用它创建嵌套范围。
1 public class ThreadCreator 2 { 3 //把父范围注入生成线程的代码 4 private ILifetimeScope _parentScope; 5 public ThreadCreator(ILifetimeScope parentScope) 6 { 7 this._parentScope = parentScope; 8 } 9 10 public void ThreadStart() 11 { 12 using (var threadLifetime = this._parentScope.BeginLifetimeScope()) 13 { 14 //开启一个线程时,在嵌套scope中解析,以此实现线程间组件的隔离 15 var thisThreadsInstance = threadLifetime.Resolve<MyThreadScopedComponent>(); 16 } 17 } 18 }
参考文章:
1、https://blog.csdn.net/WuLex/article/details/78704903
2、http://www.yuanjiaocheng.net/Autofac/instance-scope.html
3、https://www.cnblogs.com/dongbeifeng/p/autofac-instance-scope.html
阅读目录
Mvc中使用Autofac
前面学习了AutoFac的注册、解析、生命周期,这里写一个AutoFac在ASP.NET MVC中的简单使用。
基本结构:AutoFacMvc作为ui层 ,IService类库(各种服务接口),Service类库(IService中接口的实现),Model类库(数据模型,这里使用EF)
我们的目的:实现MVC中的Controller和Service中的具体实现类解耦
获取用户列表的简单例子:
IService中的接口:
namespace IService { public interface IUserInfoService { List<UserInfo> GetUsers(); } }
Service中的实现:
namespace Service { public class UserInfoService : IUserInfoService { //获取用户列表 public List<Model.UserInfo> GetUsers() { DbContext context = DbFactory.GetDbContext(); return context.Set<UserInfo>().ToList<UserInfo>(); } } }
第一步:在mvc中添加dll文件,可以通过Nuget直接添加
第二步:在App_Start文件夹中添加一个AutofacConfig类
1 public class AutofacConfig 2 { 3 /// <summary> 4 /// 负责调用autofac框架实现业务逻辑层和数据仓储层程序集中的类型对象的创建 5 /// 负责创建MVC控制器类的对象(调用控制器中的有参构造函数),接管DefaultControllerFactory的工作 6 /// </summary> 7 public static void Register() 8 { 9 //实例化一个autofac的创建容器 10 var builder = new ContainerBuilder(); 11 //告诉Autofac框架,将来要创建的控制器类存放在哪个程序集 (AutoFacMvcDemo) 12 Assembly controllerAss = Assembly.Load("AutoFacMvcDemo"); 13 builder.RegisterControllers(controllerAss); 14 15 //如果有Dal层的话,注册Dal层的组件 16 //告诉autofac框架注册数据仓储层所在程序集中的所有类的对象实例 17 //Assembly dalAss = Assembly.Load("Dal"); 18 //创建respAss中的所有类的instance以此类的实现接口存储 19 //builder.RegisterTypes(dalAss.GetTypes()).AsImplementedInterfaces(); 20 21 //告诉autofac框架注册业务逻辑层所在程序集中的所有类的对象实例 22 Assembly serviceAss = Assembly.Load("Service"); 23 //创建serAss中的所有类的instance以此类的实现接口存储 24 builder.RegisterTypes(serviceAss.GetTypes()).AsImplementedInterfaces(); 25 26 27 //创建一个Autofac的容器 28 var container = builder.Build(); 29 //将MVC的控制器对象实例 交由autofac来创建 30 DependencyResolver.SetResolver(new AutofacDependencyResolver(container)); 31 } 32 }
第三步:在Global.asax调用AutofacConfig类中的Register方法,注册组件
1 public class MvcApplication : System.Web.HttpApplication 2 { 3 protected void Application_Start() 4 { 5 AreaRegistration.RegisterAllAreas(); 6 RouteConfig.RegisterRoutes(RouteTable.Routes); 7 //注册组件 8 AutofacConfig.Register(); 9 } 10 }
第四步:简单测试
UserInfoController :
1 namespace AutoFacMvcDemo.Controllers 2 { 3 public class UserInfoController : Controller 4 { 5 private IUserInfoService userinfoService; 6 //通过构造器注入依赖 7 public UserInfoController(IUserInfoService _userinfoService) 8 { 9 userinfoService = _userinfoService; 10 } 11 12 public ActionResult Index() 13 { 14 //不使用autofac时,service层和mvc ui层强耦合 15 //UserInfoService userInfoService = new UserInfoService(); 16 //List<UserInfo> users= userInfoService.GetUsers(); 17 18 List<UserInfo> users= userinfoService.GetUsers(); 19 ViewBag.users = users; 20 return View(); 21 } 22 } 23 }
View
@{ ViewBag.Title = "Index"; } <h2>用户列表</h2> @* 简单展示用户 *@ <table> @foreach (var item in ViewBag.users) { <tr> <td>@item.UId</td> <td>@item.UserName</td> <td>@item.Age</td> </tr> } </table>
运行结果:
补充:可以逐个进行注册代码如下,在Application_Start方法中添加如下代码 :
//创建autofac管理注册类的容器实例 var builder = new ContainerBuilder(); //注册组件(注意:在注册时添加了UserInfoService的引用,其实还是耦合的,在不分层的项目中好用) builder.RegisterType<UserInfoService>().As<IUserInfoService>(); //使用Autofac提供的RegisterControllers扩展方法来对程序集中所有的Controller一次性的完成注册 builder.RegisterControllers(Assembly.GetExecutingAssembly()); //生成具体的实例 var container = builder.Build(); //下面就是使用MVC的扩展 更改了MVC中的注入方式. DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
2017-06-29 Entity Framework 4.0 recipes 读书笔记2 ExecuteStoreQuery()
2016-06-29 javascript的setTimeout()用法总结,js的setTimeout()方法