Asp.Net Core 進階(三)—— IServiceCollection依賴注入容器和使用Autofac替換它

  Asp.Net Core 提供了默認的依賴注入容器 IServiceCollection,它是一個輕量級的依賴注入容器,因此功能很少,只是提供了基礎的一些功能,要實現AOP就有點麻煩,所以在實際工做當中,咱們經常會使用第三方依賴注入容器替換掉Asp.Net Core自帶的依賴注入容器。cookie

  咱們先來看下Asp.Net Core自帶依賴注入容器IServiceCollection的主要用法,雖說在工做中常常會被替換掉,但有些狀況下使用該自帶的就已經足夠了,因此天然也就須要先了解它的使用方法。ide

  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容器。 它管理類與類之間的依賴關係,使得應用程序層級之間實現瞭解耦,不至於在應用程序變得愈來愈複雜的狀況下難以修改。ui

   那麼如今就一塊兒來看看怎麼使用Autofac來替換掉Asp.Net Core自帶的依賴注入容器吧,首先先來介紹最經常使用也是被推薦使用的構造函數注入方式。在Autofac官方文檔中有例子可參考。要使用Autofac替換IServiceCollection,咱們須要在Startup.cs文件中將ConfigureServices方法的返回值從void修改成 IServiceProvider。this

  在開始以前,咱們須要先從Nuget下載安裝Autofac,可使用以下命令進行安裝spa

Install-Package Autofac.Extensions.DependencyInjection -Version 4.4.0

  接下來隨便定義兩個測試接口和實現類code

 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方法中,單獨抽出來會顯得不那麼臃腫,優雅一些。blog

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方法了。

相關文章
相關標籤/搜索