• Asp.Net Core 进阶(三)—— IServiceCollection依赖注入容器和使用Autofac替换它


      Asp.Net Core 提供了默认的依赖注入容器 IServiceCollection,它是一个轻量级的依赖注入容器,所以功能不多,只是提供了基础的一些功能,要实现AOP就有点麻烦,因此在实际工作当中,我们常常会使用第三方依赖注入容器替换掉Asp.Net Core自带的依赖注入容器。

      我们先来看下Asp.Net Core自带依赖注入容器IServiceCollection的主要用法,虽然说在工作中经常会被替换掉,但有些情况下使用该自带的就已经足够了,所以自然也就需要先了解它的使用方法。

      IServiceCollection依赖注入生命周期和其他大多数依赖注入容器一样,分为 瞬时生命周期、单例和请求单例。我们可以在Startup.cs文件中的ConfigureServices方法中直接使用它。这里我们单独把它拿出来看一下具体怎么使用,我们定义ITestService1,ITestService2,ITestService3,ITestService4以及他们的4个实现类。

    IServiceCollection container = new ServiceCollection();
    container.AddTransient<ITestService1, TestService1>();//瞬时生命周期 
    container.AddSingleton<ITestService2, TestService2>();//单例:全容器都是一个
    container.AddScoped<ITestService3, TestService3>();//请求单例:一个请求作用域是一个实例
    container.AddSingleton<ITestService4>(new TestService4());
    
    var provider = container.BuildServiceProvider();
    ITestService1 testService1 = provider.GetService<ITestService1>();
    ITestService1 testService2 = provider.GetService<ITestService2>();
    Console.WriteLine(object.ReferenceEquals(testService1, testService2));//输出 false
    
    ITestService2 testService2_1 = provider.GetService<ITestService2>();
    ITestService2 testService2_2 = provider.GetService<ITestService2>();
    Console.WriteLine(object.ReferenceEquals(testService2_1, testService2_2));//输出 true
    
    ITestService3 testService3_1 = provider.GetService<ITestService3>();
    ITestService3 testService3_2 = provider.GetService<ITestService3>();
    Console.WriteLine(object.ReferenceEquals(testService3_1, testService3_2));//输出 true
    
    var scope1 = provider.CreateScope();
    var scope2 = provider.CreateScope();
    ITestService3 testService3_3 = scope1.ServiceProvider.GetService<ITestService3>();
    ITestService3 testService3_4 = scope2.ServiceProvider.GetService<ITestService3>();
    Console.WriteLine(object.ReferenceEquals(testService3_3, testService3_4)); //输出 false
    
    ITestService4 testService4_1 = provider.GetService<ITestService4>();
    ITestService4 testService4_2 = provider.GetService<ITestService4>();
    Console.WriteLine(object.ReferenceEquals(testService4_1, testService4_2)); //输出 true

       上述代码已经可以很好的阐述了IServiceCollection的用法,但是这些也只是基本的功能,接下来我们就来看下使用Autofac如何替换掉IServiceCollection。

       Autofac是一个Microsoft .NET的IoC容器。 它管理类与类之间的依赖关系,使得应用程序层级之间实现了解耦,不至于在应用程序变得越来越复杂的情况下难以修改。

       那么现在就一起来看看怎么使用Autofac来替换掉Asp.Net Core自带的依赖注入容器吧,首先先来介绍最常用也是被推荐使用的构造函数注入方式。在Autofac官方文档中有例子可参考。要使用Autofac替换IServiceCollection,我们需要在Startup.cs文件中将ConfigureServices方法的返回值从void修改为 IServiceProvider。

      在开始之前,我们需要先从Nuget下载安装Autofac,可以使用如下命令进行安装

    Install-Package Autofac.Extensions.DependencyInjection -Version 4.4.0

      接下来随便定义两个测试接口和实现类

     public interface ITestServiceA
        {
            void Show();
        }
    
        public interface ITestServiceB
        {
            void Show();
        }
    
        public class TestServiceA : ITestServiceA
        {
            public   void Show()
            {
                Console.WriteLine("This is TestServiceA....");
            }
        }
    
        public class TestServiceB : ITestServiceB
        {
            public void Show()
            {
                Console.WriteLine("This is TestServiceB....");
            }
        }

      接下来我们修改Startup.cs的ConfigureServices方法

    // This method gets called by the runtime. Use this method to add services to the container.
    public IServiceProvider ConfigureServices(IServiceCollection services)
    {
        services.Configure<CookiePolicyOptions>(options =>
        {
            // This lambda determines whether user consent for non-essential cookies is needed for a given request.
            options.CheckConsentNeeded = context => true;
            options.MinimumSameSitePolicy = SameSiteMode.None;
        });
        services.AddSession();
        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
    
        var containerBuilder = new ContainerBuilder();
    containerBuilder.RegisterModule
    <AutofacInitModule>();
    // Populate 方法是最重要的,如果没有调用这个方法,则无法将注入到 IServiceCollection的内置对象填充到autofac中,像控制器注入,Log注入等,程序会报错。
    containerBuilder.Populate(services); var container = containerBuilder.Build(); return new AutofacServiceProvider(container); }

      AutofacInitModule类是继承了Autofac.Module类的子类,我们可以重写Load方法进行Autofac的初始化注入,当然也可以直接写在Startup文件的ConfigureServices方法中,单独抽出来会显得不那么臃肿,优雅一些。

    using Autofac;
    
    namespace WebApplication2
    {
        internal class AutofacInitModule : Module
        {
            protected override void Load(ContainerBuilder builder)
            {
                builder.RegisterType<TestServiceA>().As<ITestServiceA>();
                builder.RegisterType<TestServiceB>().As<ITestServiceB>();
    
                // builder.RegisterType<TestServiceA>().As<ITestServiceA>().SingleInstance(); //单例
            }
        }
    }

      现在我们就可以在Controller中使用了

    public class HomeController : Controller
    {
        private readonly ILogger<HomeController> _logger;
        private readonly ITestServiceA _serviceA;
        private readonly ITestServiceB _serviceB;
    
    
        public HomeController(ILogger<HomeController> logger, ITestServiceA serviceA, ITestServiceB serviceB)
        {
            this._logger = logger;
            this._serviceA = serviceA;
            this._serviceB = serviceB;
        }
    
        public IActionResult Index()
        {
            this._serviceA.Show();
            this._serviceB.Show();
            this._logger.LogWarning("this is logger....");
            return View();
        } 
    }

      运行程序,可以看到_logger、_serviceA、_serviceB都成功的被创建了。

      那么上述是使用的autofac的构造函数注入,虽然是最被推荐的也是最常用的注入方式,但是autofac也提供了属性注入的方式,下面我们也了解一下。

      修改Startup.cs的ConfigureServices方法和AutofaceInitModule类

    // This method gets called by the runtime. Use this method to add services to the container.
    public IServiceProvider ConfigureServices(IServiceCollection services)
    {
        services.Configure<CookiePolicyOptions>(options =>
        {
            // This lambda determines whether user consent for non-essential cookies is needed for a given request.
            options.CheckConsentNeeded = context => true;
            options.MinimumSameSitePolicy = SameSiteMode.None;
        });
        services.AddSession();
        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2).AddControllersAsServices();
    
        var containerBuilder = new ContainerBuilder();
    
        // Populate 方法是最重要的,如果没有调用这个方法,则无法将注入到 IServiceCollection的内置对象填充到autofac中,像控制器注入,Log注入等,程序会报错。
        containerBuilder.Populate(services);
    
        containerBuilder.RegisterModule<AutofacInitModule>();
    
        var container = containerBuilder.Build();
        return new AutofacServiceProvider(container);
    }
    using Autofac;
    using Microsoft.AspNetCore.Mvc;
    using System.Linq;
    
    namespace WebApplication2
    {
        internal class AutofacInitModule : Module
        {
            protected override void Load(ContainerBuilder builder)
            {
                //注册服务
                builder.RegisterType<TestServiceA>().As<ITestServiceA>().PropertiesAutowired();
                builder.RegisterType<TestServiceB>().As<ITestServiceB>().PropertiesAutowired();
    
                //注册所有控制器
                var controllersTypesInAssembly = typeof(Startup).Assembly.GetExportedTypes()
                .Where(type => typeof(ControllerBase).IsAssignableFrom(type)).ToArray();
    
                builder.RegisterTypes(controllersTypesInAssembly).PropertiesAutowired();
    
                // builder.RegisterType<TestServiceA>().As<ITestServiceA>().SingleInstance(); //单例
            }
        }
    }

    需要注意的是 services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2).AddControllersAsServices();和

    containerBuilder.Populate(services);需要写在注入服务之前,属性注入才能成功。

        public class HomeController : Controller
        {
            //private readonly ILogger<HomeController> _logger;
            //private readonly ITestServiceA _serviceA;
            //private readonly ITestServiceB _serviceB;
    
            public ILogger<HomeController> Logger { get; set; }
            public ITestServiceA ServiceA { get; set; }
            public ITestServiceB ServiceB { get; set; }
    
            //public HomeController(ILogger<HomeController> logger, ITestServiceA serviceA, ITestServiceB serviceB)
            //{
            //    this._logger = logger;
            //    this._serviceA = serviceA;
            //    this._serviceB = serviceB;
            //}
    
            public IActionResult Index()
            {
                //this._serviceA.Show();
                //this._serviceB.Show();
                //this._logger.LogWarning("this is logger....");
    
                Logger.LogWarning(ServiceA.Show());
                Logger.LogWarning(ServiceB.Show());
    
                return View();
            } 
        }

      运行可以看到成功的结果

      最后,在一开始之前,我们提到Asp.Net Core自带的依赖注入容器在实现AOP的功能很麻烦,在工作中常常会替换成第三方的依赖注入容器,那么现在我们再来看一下autofac怎么实现AOP。

      Autofac实现AOP需要引入Autofac.Extras.DynamicProxy库,通过Nuget添加该库

    Install-Package Autofac.Extras.DynamicProxy -Version 4.5.0

      接着为了实现AOP,我们定义如下类和接口

    using Autofac.Extras.DynamicProxy;
    using Castle.DynamicProxy;
    using Microsoft.Extensions.Logging;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    
    namespace WebApplication2
    {
        public class AutofacTestAop : IInterceptor
        {
            public void Intercept(IInvocation invocation)
            {
                Console.WriteLine($"invocation.Methond={invocation.Method}, invocation.Arguments={string.Join(",", invocation.Arguments)}");
    
                invocation.Proceed(); //继续执行TestAop.Show
    
                Console.WriteLine($"Method {invocation.Method} Excuted");
            }
        }
    
        public interface ITestAop
        {
            void Show();
        }
    
        [Intercept(typeof(AutofacTestAop))]
        public class TestAop : ITestAop
        {
            private ILogger<TestAop> _logger;
    
            public TestAop(ILogger<TestAop> logger)
            {
                this._logger = logger;
            }
    
            public void Show()
            {
                this._logger.LogWarning("this is TestAop .....");
            }
        }
    }

      AutofacTestAop为实现了IInterceptor的类,它的Intercept方法就是用来做AOP的调用的。除了实现这个方法外,在需要添加AOP功能的类上需要添加特性 [Intercept(typeof(AutofacTestAop))] 。最后需要在AutofacInitModule中配置一下启用 EnableInterfaceInterceptors。

    using Autofac;
    using Autofac.Extras.DynamicProxy;
    using Microsoft.AspNetCore.Mvc;
    using System.Linq;
    
    namespace WebApplication2
    {
        internal class AutofacInitModule : Module
        {
            protected override void Load(ContainerBuilder builder)
            {
                //注册服务
                builder.RegisterType<TestServiceA>().As<ITestServiceA>();
                builder.RegisterType<TestServiceB>().As<ITestServiceB>();
    
                builder.Register(c => new AutofacTestAop());
                builder.RegisterType<TestAop>().As<ITestAop>().EnableInterfaceInterceptors();
    
                // builder.RegisterType<TestServiceA>().As<ITestServiceA>().SingleInstance(); //单例
            }
        }
    }

      最后在HomeController中调用ITestAop的Show方法就会先执行AutofacTestAop里的Intercept方法了。

  • 相关阅读:
    基础JavaScript练习(一)总结
    零基础JavaScript编码(三)总结
    零基础JavaScript编码(二)
    零基础JavaScript编码(一)
    响应式网格(栅格化)布局总结
    实现常见的技术产品官网的页面架构及样式布局总结
    通过HTML及CSS模拟报纸排版总结
    任务四:定位和居中问题总结
    1. Two Sum
    任务三:三栏式布局总结
  • 原文地址:https://www.cnblogs.com/jesen1315/p/11074997.html
Copyright © 2020-2023  润新知