1 概述

ASP.NET Core在启动以及后续针对每个请求的处理过程中的各个环节都需要相应的组件提供相应的服务,为了方便对这些组件进行定制, ASP.NET Core通过定义接口的方式对它们进行了“标准化”,我们将这些标准化的组件称为服务, ASP.NET Core在内部专门维护了一个IOC容器来提供所需的服务。服务的创建到销毁的过程完全交给IOC容器,大大降低了耦合度。

.net 依赖注入 .netcore依赖注入_.net 依赖注入

依赖注入旨在实现针对服务对象的动态提供。具体来说,服务的消费者利用一个独立的容器(Container)来获取所需的服务对象,容器自身在提供服务对象的过程中会自动完成依赖的解析与注入。话句话说,由DI容器提供的这个服务对象是一个” 开箱即用”的对象,这个对象自身直接或者间接依赖的对象已经在初始化的工程中被自动注入其中了。框架负责创建依赖关系的实例,并在不再需要时将其释放。

核心组件说明

依赖注入有两个核心组件:IServiceCollectionIServiceProvider

.net 依赖注入 .netcore依赖注入_.net 依赖注入_02

public void ConfigureServices(IServiceCollection services){
   }

    public interface IServiceCollection : IList<ServiceDescriptor>, ICollection<ServiceDescriptor>, IEnumerable<ServiceDescriptor>, IEnumerable
    {
    }

IServiceCollection,是一个实现了IList的服务描述类,负责注册实例,即服务类型【接口】与服务实现。这个组件在startup中的ConfigureServices方法的参数中

IServiceProvider 简单的内置容器,通过IServiceCollection的拓展方法BuildServiceProvider()创建,意即根据服务描述信息来管理服务组件的整个生命周期。消费者在消费服务组件时,就可以轻松地从容器中根据已经注册的服务描述信息获得服务实例。

public void ConfigureServices(IServiceCollection services)
        {
            //IServiceCollection ICollection<ServiceDescriptor>的服务描述实现类,一系列方法注册服务的描述信息,包括服务类型、对应的服务实例、服务的生命周期等
            //AddSingleton<TService, TImplementation> TService 服务类型,一般是接口形式,TImplementation 具体的实现类型。一个服务类型可对应多个服务实现类型。
            services.AddSingleton<IUser, User>();


            services.AddSingleton<IComputerBrand, HuaWeiComputer>()
                    .AddSingleton<IComputerBrand, LenovoComputer>();

          

            //服务类型IUser,具体实现通过自定义类注册。
            services.AddSingleton<IUser>(x => new User
            {
                Name = "lyf"
            });

            Console.WriteLine("IOC  registered {0} services definition ", services.Count());

            //拓展方法 注册MVC组件
            services.AddMvc(options => options.EnableEndpointRouting = false);

            //返回ServiceProvider 是AspNetCore内置的IOC容器,默认是通过IServiceCollection创建,读取已注册的服务描述信息,创建服务实例
            var ioc = services.BuildServiceProvider();


            //从容器中获得注册的服务信息
            IEnumerable<IComputerBrand> brands = ioc.GetServices<IComputerBrand>();
            Console.WriteLine("IOC  registered {0} type of computer brand", brands.Count());


            var computer = ioc.GetRequiredService<IComputerBrand>();
            Console.WriteLine("Computerbrand:{0}", computer.ToString());


            ioc.GetService<IUser>();

        }

AspNetCore默认使用这个容器去管理服务,所以忽略这个BuildServiceProvider()方法。因此在这个ConfigServices方法中,更主要的是去关注服务的注册,如何去绑定服务类型与实例的关系。

服务注册方法

常用的注册方式是AddSington、AddScope、AddTranisent,分别在容器中注册单例的、暂时、瞬时的服务。以注册单例服务AddSington说明【常用方法】。其他的服务注册用法是类似的。

// TService 服务类型,一般是接口形式,TImplementation 具体的实现类型。一个服务类型可对应多个服务实现类型。
AddSingleton<TService, [DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.PublicConstructors)] TImplementation>(this IServiceCollection services)
            where TService : class
            where TImplementation : class, TService;


//TService 服务类型,一般是接口形式 ,通过回调工厂去创建实现类
AddSingleton<TService>(this IServiceCollection services, Func<IServiceProvider, TService> implementationFactory) where TService : class;

构造器函数注入

在实际消费者在消费服务组件时,并不会不直接使用从容器中GetService的方法去获得服务。这样子会暴露容器中所有注册的组件信息,并且需要先取得IOC 容器对象,这个步骤不非必要的。
构造器注入【项目中最常见的使用方法,暂不支持方法注入与属性注入的方法】,在构造函数中借助参数将依赖的对象注入到创建的对象之中。通过调用构造函数的方式来创建服务实例时,传入构造函数的所有参数必须先被初始化。存在多个构造函数数时,将会选择所有参数被实例化,而且参数值是最多的一个构造函数。如果未找到指定的构造函数,那么服务无法获得,服务抛出异常。

[Route("/api/[controller]/[action]")]
    public class ComputerController:ControllerBase
    {
        private IEnumerable<IComputerBrand> brands;
        /// <summary>
        /// 构造器函数注入,可以给类中的brands对象通过构造器传递参数并赋值
        /// </summary>
        /// <param name="brands"></param>
        public ComputerController(IEnumerable<IComputerBrand> brands) {
            //赋值后,对象不为空
            this.brands = brands;
        }
        public void ShowBrand() {
            foreach (IComputerBrand computer in brands) {
                Console.WriteLine(computer.ToString()+"\n");
            }
        }
    }

除了加入自定义的服务之外,显然不够我们处理HTTP请求以及其他的业务功能,在项目初始化后将会注入默认的服务。在Program中通过默认配置创建服务器时,会注册一些默认的服务。这些服务和我们自行注册的服务并没有任何区别,只要我们知道对应的服务类型,就可以通过构造器函数注入的方式获取并使用它们。

  • ILoggerFactory
  • ILogger<>
  • IApplicationBuilderFactory
  • IHttpContextFactory
  • IOptions<>
  • IStartupFilter
  • Istartup
  • IHostingEnvironment

如果我们需要这些预注册的服务,我们可以通过构造函数注入的方式来使用它们。

AutoFac替换内置的IOC容器

ASP.NET Core内置的IOC容器默认的实现对于一些小型的项目完全够用,对于大型项目而言,对象的依赖关系复杂,内置的IOC容器难以管理对象的创建于对象的生命周期。原因在于只提供了最基本的AddXXXX方法来注册服务实例,因此项目中将会出现很多这样的代码。为了简化此步骤,可使用第三方IOC容器替换内置的IOC容器,如Autofac。

步骤一:下载Autofac的Nuget包,这里netcore 3.1下载的是autofac8.00的版本,可自行下载对应的版本。
步骤二: Program类中修改默认注入方式,采用Autofac
步骤二: 新增服务注册类,多种方法注册服务描述信息
步骤四:修改配置类StartUp,增加ConfigureContainer方法,
将注册在AutoFac的服务描述实例化到容器中。

替换内置的IOC容器,采用autofac

public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                //Autofac替换默认的IOC容器
                .UseServiceProviderFactory(new AutofacServiceProviderFactory())
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });

新增服务注册类

public class AutoFacModule : Module
    {
        /// <summary>
        /// AutoFac注册类
        /// </summary>
        /// <param name="builder"></param>
        protected override void Load(ContainerBuilder builder)
        {
           // ContainerBuilder  是一个可以将IServiceCollection 服务描述类转化为容器的中间类
           //使用ContainerBuilder 加入服务注册信息
            builder.RegisterType<HuaWeiComputer>().As<IComputerBrand>();
            builder.RegisterType<LenovoComputer>().As<IComputerBrand>();

        }
    }

修改startUp

//autofac 新增
        public ILifetimeScope AutofacContainer { get; private set; }

        //autofac 新增
        public void ConfigureContainer(ContainerBuilder builder)
        {
            // 直接用Autofac注册我们自定义的 
            builder.RegisterModule(new AutoFacModule());
        }
netcore 3.X版本与2.X 整合AutoFac框架存在些许差异,后者在注册服务时是直接返回一个可以替换原生IOC容器IServiceProvider。而不是void 无返回,采用内置的IOC容器
 public IServiceProviderConfigureServices(IServiceCollection services){
 }