zoukankan      html  css  js  c++  java
  • asp.net core 四 IOC&DI Autofac

    其实关于IOC,DI已经有了很多的文章,但是自己在使用中还是有很多困惑,而且相信自己使用下,印象还是会比较深刻的
    关于这段时间一直在学习.net core,但是这篇文章是比较重要的,也是我自己觉得学习的东西非常多的,也得到了大神的指教,在这里和大家分享下
    什么是IOC?
      控制反转(Inversion of Control,英文缩写为IoC)把创建对象的权利交给框架,是框架的重要特征,并非面向对象编程的专用术语。它包括依赖注入(Dependency Injection,简称DI)和依赖查找(Dependency Lookup),上面的来源于百度
      在做程序设计时,考虑到程序的耦合性,高扩展等问题,还是尽量需要将程序抽象化,各层的业务不再有实际的依赖关系,全部依赖于抽象也就是接口,在这种设计的情况下,接口的具体实现的创建工作最好交由IOC框架来做,或者自己扩展一个Ioc架构,完成一个构建工厂的功能,其实ico的工作就是一个产生对象的工厂,依赖于反射的技术
      下面讲讲.net core,下面直接程序为core了,core框架内部包含自己的ioc框架,本文从两方面来讲,首先是自带的ioc,第二是第三方ioc(actofac),文章后面有源码
      一.自带的IOC
          1.定义接口以及实现           
    /// <summary>
        /// 动物类
        /// </summary>
        public interface Animal
        {
            string Call();
        }
     
        /// <summary>
        /// 狗狗类
        /// </summary>
        public class Dog : Animal
        {
            public Dog()
            {
                this.Name = Guid.NewGuid().ToString();
            }
            public string Name { get; set; }
     
            public string Call()
            {
                return this.Name;
            }
        }
          2.注册到ioc中
     // This method gets called by the runtime. Use this method to add services to the container.
            public void ConfigureServices(IServiceCollection services)
            {
                services.AddMvc();
                services.AddTransient<Animal, Dog>();
                //services.AddScoped<Animal, Dog>();
                //services.AddSingleton<Animal, Dog>();
            }
      该方法在Startup.cs
      3.在api中注入,并使用   
      
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Linq;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Mvc;
     
    namespace CM.NetCoreIOC.Controllers
    {
        public class HomeController : Controller
        {
            Animal animal1;
            Animal animal2;
            public HomeController(Animal animal1, Animal animal2)
            {
                this.animal1 = animal1;
                this.animal2 = animal2;
            }
            public string Index()
            {
                return $"Animal 1 Name:{animal1.Call()} Animal 2 Name:{animal2.Call()}";
            }
        }
    }
     注意这里的需要提供构造函数,将需要注入的作为构造函数参数,访问接口得到结果,刷新下页面,然后两次结果不一样,而且每次的Animal1与Animal2不一样
     
     
     这里的两个Animal不一样,什么原因?是因为我们注册的选择方法决定的,services.AddTransient,那有没有其他选项呢?有,如下,我们一个个来做实验

    用Singleton注册
    services.AddMvc();
                //services.AddTransient<Animal, Dog>();
                services.AddScoped<Animal, Dog>();
                //services.AddSingleton<Animal, Dog>();
                
      结果:
     
      看出区别了吧,两次结果不一样,但是每次请求的Animal 1 与Animal2一样啊,是不是发现有了不同的应用场景,嘿嘿
      用AddSingleton注册  
      services.AddMvc();
                //services.AddTransient<Animal, Dog>();
                //services.AddScoped<Animal, Dog>();
                services.AddSingleton<Animal, Dog>();
        
       是不是有发现了点什么?单例模式,创建单例的方式更加简单了
       默认的使用其实很简单,也还比较方便
     
     二.第三方IOC(autofac)
       1.添加Nuget引用 Autofac ,Autofac.Extensions.DependencyInjection
         
         
       2.修改Startup.cs文件, ConfigureServices 方法,从void变为 IServiceProvider       
    // This method gets called by the runtime. Use this method to add services to the container.
            public IServiceProvider ConfigureServices(IServiceCollection services)
            {
                services.AddMvc();
                var builder = new ContainerBuilder();           
                builder.RegisterType(typeof(Dog)).As(typeof(Animal))
                            .InstancePerLifetimeScope()
                            .PropertiesAutowired();
                builder.Populate(services);
                return new AutofacServiceProvider(builder.Build());
            }
            
      运行得到结果:
     
      两次不一样,每次的对象却是一样的,达到了我们的逾期效果,这里大家不知道有没有类似的疑问?为什么可以做到?
      官网的说明,想要获取依赖注入的对象实例,有两种方法,自己也做了实验,如下,修改Startup.cs,修改Configure方法  
     
    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
            public void Configure(IApplicationBuilder app, IHostingEnvironment env)
            {
                if (env.IsDevelopment())
                {
                    app.UseBrowserLink();
                    app.UseDeveloperExceptionPage();
                }
                else
                {
                    app.UseExceptionHandler("/Home/Error");
                }
     
                app.UseStaticFiles();
     
                app.UseMvc(routes =>
                {
                    routes.MapRoute(
                        name: "default",
                        template: "{controller=Home}/{action=Index}/{id?}");
                });
                var animal1 = ActivatorUtilities.GetServiceOrCreateInstance(app.ApplicationServices, typeof(Animal));
                var animal2 = app.ApplicationServices.GetService(typeof(Animal));
            }

     调试看看两个对象animal1与animal2 

     两者内部还是依赖于IServiceProvider接口来实现的,autofac写了一个AutofacServiceProvider实现了IServiceProvider,从而替换掉内部默认的ServiceProvider,所以达到了效果
       一直没有提的是core下面的ioc不支持属性注入,只能通过构造函数注入,也就是说core默认的ioc,你要注入,就要把参数全部写在构造函数的参数中,但是autofac是支持属性注入的,PropertiesAutowired就是已属性方式注入,那我们来试试,把HomeController的构造函数干掉看看
      
      报错了,根本没有注入两个属性,怎么回事?.....不对我们根本还没注册Controller到autofac中,为什么会有对象自己生成啊,其实这里的情况是比较特殊的,如果我们这时候不是直接在Controller层做实验,其实已经完成了属性的注册了,因为这时候Controller的创建工作还不是autofac做的,从我没有注册就可以看出来,那是什么原因啊?我先注册看看  

    // This method gets called by the runtime. Use this method to add services to the container.
            public IServiceProvider ConfigureServices(IServiceCollection services)
            {          
                services.AddMvc();
                var builder = new ContainerBuilder();
                builder.RegisterType(typeof(Dog)).As(typeof(Animal))
                            .InstancePerLifetimeScope()
                            .PropertiesAutowired();
                builder.RegisterType(typeof(HomeController))
                            .InstancePerLifetimeScope()
                            .PropertiesAutowired();
                builder.Populate(services);
                return new AutofacServiceProvider(builder.Build());
            }

    还是一样报错,开始查资料了,不是说autofac可以属性注入吗?
    查了资料之后发现需要在ConfigureServices 方法加入一句代码 services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>());这样才真正的替换为autufac,才支持属性注入
    // This method gets called by the runtime. Use this method to add services to the container.
            public IServiceProvider ConfigureServices(IServiceCollection services)
            {
                services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>());
                services.AddMvc();
                var builder = new ContainerBuilder();
                builder.RegisterType(typeof(Dog)).As(typeof(Animal))
                            .InstancePerLifetimeScope()
                            .PropertiesAutowired();
                builder.RegisterType(typeof(HomeController))
                            .InstancePerLifetimeScope()
                            .PropertiesAutowired();
                builder.Populate(services);
                return new AutofacServiceProvider(builder.Build());
            }

    运行效果:

     终于成功了,但是我的Controller也做了相应的修改的
     
    public class HomeController : Controller
        {
            public Animal animal1 { get; set; }
            public Animal animal2 { get; set; }
            //public HomeController(Animal animal1, Animal animal2)
            //{
            //    this.animal1 = animal1;
            //    this.animal2 = animal2;
            //}
            public string Index()
            {
                return $"Animal 1 Name:{animal1.Call()} Animal 2 Name:{animal2.Call()}";
            }
        }
     属性必须提供get;set;方法,必须是public
     回到上面的问题,必须要添加 services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>()) ,这里的替换方法其实来源于
     services.AddMvc().AddControllersAsServices();
     AddControllerAsServices 源码
    public static IMvcBuilder AddControllersAsServices(this IMvcBuilder builder)
    {
        var feature = new ControllerFeature(); 
        builder.PartManager.PopulateFeature(feature); 
        foreach (var controller in feature.Controllers.Select(c => c.AsType())) 
        { 
            builder.Services.TryAddTransient(controller, controller); 
        }
        builder.Services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>()); 
        return builder; 
    }
    其实内部就是就是将IControllerAcivator替换为ServiceBasedControllerActivator,
    通过查看源代码ASP.NET Core默认使用DefaultControllerActivator类对Controller进行创建工作;但是找到这个类的Create函数发布它其实调用的是ActivatorUtilities来创建对象的。前面也说过这个的话,在创建类型对象时,IServiceProvdier只负责对构造器中的参数进行查找注入,创建对象的操作还是由ActivatorUtilities来create出来的,这样也就没用利用上autofac替换的ServiceProvider,也就是说ActivatorUtilities并没有扩展点来使用我们提供的方法进行替换,所以才造成了无法注入的问题。
    所以需要把Controller的创建权转接到autofac,把IControllerAcivator替换为ServiceBasedControllerActivator就可以了?下面是ServiceBasedControllerActivator的Create方法
    public object Create(ControllerContext actionContext)
    {
    if (actionContext == null)
    {
    throw new ArgumentNullException(nameof(actionContext));
    }
    var controllerType = actionContext.ActionDescriptor.ControllerTypeInfo.AsType();
    return actionContext.HttpContext.RequestServices.GetRequiredService(controllerType);
    }
    这里的RequestServices就是IServiceProvider,所以到这里终于明白了,为什么一句代码就接管了controller的创建
    至此,.net core ioc就写完了,但是autofac的使用以及ioc的内容还有很多东西要学习,将在其他文章来学习.
     
  • 相关阅读:
    c++中static的使用
    sublime3 ctl+b无效
    Maximum Subarray
    Find the Duplicate Number
    Reverse Linked List
    c++ primer 2 变量和基本类型
    Single Number II
    Roman to Integer & Integer to Roman
    Search Insert Position
    Unique Binary Search Trees II
  • 原文地址:https://www.cnblogs.com/tiaoshuidenong/p/8602737.html
Copyright © 2011-2022 走看看