zoukankan      html  css  js  c++  java
  • 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));
    复制代码
  • 相关阅读:
    Oracle-增加字段
    Oracle数据库将varchar类型的字段改为Clob类型
    将Oracle数据库字段长度进行修改
    http请求util
    读取excel文件后,将一行数据封装成一个对象,多行返回一个map对象即可
    使用tushare 库查阅交易日历
    python winsound模块
    python可视化:matplotlib系列
    期货、股指期权、ETF期权
    股指期货
  • 原文地址:https://www.cnblogs.com/Alex80/p/13207181.html
Copyright © 2011-2022 走看看