其实关于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的内容还有很多东西要学习,将在其他文章来学习.
 

asp.net core 四 IOC&DI Autofac的更多相关文章

  1. ASP.NET Core修改IOC为Autofac

    如下是我为了了解如何更换ASP.NET Core中的IOC而查找的文章,如果大家英文OK的,可以直接前往阅读,同时也已经有简单的github例子供大家参考. 参考文章: ASP.NET Core文档: ...

  2. 浅谈ASP.NET Core中的DI

    DI的一些事 传送门马丁大叔的文章 什么是依赖注入(DI: Dependency Injection)?     依赖注入(DI)是一种面向对象的软件设计模式,主要是帮助开发人员开发出松耦合的应用程序 ...

  3. ASP.Net Core 3.1 With Autofac ConfigureServices returning an System.IServiceProvider isn't supported.

    ASP.Net Core 3.1 With Autofac ConfigureServices returning an System.IServiceProvider isn't supported ...

  4. 浅谈ASP.NET Core中IOC与DI的理解和使用

    说起IOC和DI,使用过ASP.NET Core的人对这两个概念一定不陌生,早前,自己也有尝试过去了解这两个东西,但是一直觉得有点很难去理解,总觉得对其还是模糊不清,所以,趁着今天有空,就去把两个概念 ...

  5. Asp.net Core依赖注入(Autofac替换IOC容器)

    ASP.NET Core ASP.NET Core (previously ASP.NET 5) 改变了以前依赖注入框架集成进ASP.NET的方法. 以前, 每个功能 - MVC, Web API, ...

  6. ASP.NET Core 依赖注入(DI)

    ASP.NET Core的底层设计支持和使用依赖注入.ASP.NET Core 应用程序可以利用内置的框架服务将服务注入到启动类的方法中,并且应用程序服务也可以配置注入.由ASP.NET Core 提 ...

  7. ASP.NET Core依赖注入(DI)

    ASP.NET Core允许我们指定注册服务的生存期.服务实例将根据指定的生存时间自动处理.因此,我们无需担心清理此依赖关系,他将由ASP.NET Core框架处理.有如下三种类型的生命周期. 关于依 ...

  8. ASP.NET Core 四种释放 IDisposable 对象的方法

    本文翻译自<Four ways to dispose IDisposables in ASP.NET Core>,由于水平有限,故无法保证翻译完全正确,欢迎指出错误.谢谢! IDispos ...

  9. ASP.NET Core 四种方式绑定枚举值

    前言 本节我们来讲讲在ASP.NET Core MVC又为我们提供了哪些方便,之前我们探讨过在ASP.NET MVC中下拉框绑定方式,这节我们来再来重点看看枚举绑定的方式,充分实现你所能想到的场景,满 ...

随机推荐

  1. 对于java中的"\"和"/" 区别

    "\"在mac系统和类Unix 系统中是识别不出来的,对于java这种跨平台的语言来说不宜使用这个符号 "/"使用这个符号一般 都可以被识别

  2. cache缓存的BUG

    坑: 1.在使用这个模版代码开发的时候,当我们改变了数据库表的设置的时候,我们都要把本地的cache缓存文件删除一下. 如果不删除的话,当我们改变数据库设置的之后,程序读取数据是从本地的缓存文件里面读 ...

  3. DataTable筛选某列最大值

    dt.Compute("max(列名)",""); Compute函数的参数就两个:Expression,和Filter. Expresstion是计算表达式, ...

  4. JDBC各种数据库连接方式

    1)连接Oracle 8/8i/9i/10g/11g(thin模式) Class.forName("oracle.JDBC.driver.OracleDriver").newIns ...

  5. B+索引、Hash索引、数据类型长度

    1.为什么在数据库中要用B树索引而不是Hash索引? Mysql Hash索引结构的特殊性,其检索效率非常高,索引的检索可以一次定位,不像B-Tree 索引需要从根节点到枝节点,最后才能访问到页节点这 ...

  6. dedecms 在php5.4以上环境下 无法保存中文

    dedecms gbk版本在php5.4以上环境下 无法保存中文 原因在于5.4.0对这个函数的变化:htmlspecialchars5.4.0   The default value for the ...

  7. RFM模型及R语言实现

    每每以为攀得众山小,可.每每又切实来到起点,大牛们,缓缓脚步来俺笔记葩分享一下吧,please~ --------------------------- 一.基本概念 根据美国数据库营销研究所Arth ...

  8. WINDOWS的错误代码对应的故障

    WINDOWS的错误代码对应的故障 0000 操作已成功完成. 0001 错误的函数. 0002 系统找不到指定的文件. 0003 系统找不到指定的路径. 0004 系统无法打开文件. 0005 拒绝 ...

  9. C#:几种数据库的大数据批量插入

    在之前只知道SqlServer支持数据批量插入,殊不知道Oracle.SQLite和MySql也是支持的,不过Oracle需要使用Orace.DataAccess驱动,今天就贴出几种数据库的批量插入解 ...

  10. SCRIPT5007:无法获取属性“show”的值,对象为null或未定义

    1.错误描述 SCRIPT5007:无法获取属性"show"的值,对象为null或未定义            dojo.js,行15.字符11808 2.错误原因    requ ...