在ASP.NET Core中使用Autofac增強容器能力

前言

當前引入了NuGet包

$ Install-Package Autofac.Extras.DynamicProxy -Version 4.5.0
$ Install-Package Autofac.Extensions.DependencyInjection -Version 5.0.1

一、什麼情況需要引入第三方容器組件

  • 基於名稱的注入 - 按照名稱來區分它不同的實現的時候
  • 屬性注入 - 直接把服務註冊到某一個類的屬性中
  • 子容器
  • 基於動態代理的AOP

二、用Autofac覆蓋默認的Ioc

在引入兩個 Autofac 的包後
Program 中註冊 Autofac ,使用 UseServiceProviderFactory() 方法

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
        	// 重寫用於創建服務提供程序的工廠
        	// 把AutofacServiceProviderFactory註冊進去
            .UseServiceProviderFactory(new AutofacServiceProviderFactory())
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });

然後在 Startup 中添加一個 ConfigureContainer() 方法
原本存在的 ConfigureServices() 方法是注入默認的容器,
服務註冊進默認的容器以後會被 Autofac 接替,
然後執行 ConfigureContainer() 方法。

三、使用方式

用Autofac覆蓋默認的Ioc後

1) 普通註冊

使用 Autofac 的註冊方式和 默認的Ioc 框架註冊方式略有不同。

	public void ConfigureContainer(ContainerBuilder builder)
    {
    	// 先註冊具體實現,然後是標記爲哪個服務類型
		builder.RegisterType<T>().As<IT>();
	}

使用時

	public void Configure(IApplicationBuilder app)
	{
		// 也可以直接在構造函數中請求 ILifetimeScope  
		ILifetimeScope AutofacContainer = app.ApplicationServices.GetAutofacRoot();
		IT t = AutofacContainer.Resolve<IT>();
	}

2) 基於名稱註冊

當我們需要將一個服務註冊多次,並用不同命名來區分時

	public void ConfigureContainer(ContainerBuilder builder)
    {
    	// 使用Named
		builder.RegisterType<T>().Named<IT>("service2");
	}

使用時

	public void Configure(IApplicationBuilder app)
	{
		// 也可以直接在構造函數中請求 ILifetimeScope  
		ILifetimeScope AutofacContainer = app.ApplicationServices.GetAutofacRoot();
		IT t = AutofacContainer.ResolveNamed<IT>("service2")
	}

3) 屬性注入

屬性注入使用 PropertiesAutowired() 方法

	#region Class
	public class T: IT
    {
        public Attribute Attr { get; set; }
    }
    
	public class Attribute { }
	#endregion
	
	public void ConfigureContainer(ContainerBuilder builder)
    {
    	// 屬性注入
    	builder.RegisterType<Attribute>();
		builder.RegisterType<T>().As<IT>().PropertiesAutowired()
	}

使用時 Attr 屬性不爲 null

	public void Configure(IApplicationBuilder app)
	{
		// 也可以直接在構造函數中請求 ILifetimeScope  
		ILifetimeScope AutofacContainer = app.ApplicationServices.GetAutofacRoot();
		// t 的 Attr 屬性不爲 null
		IT t = AutofacContainer.Resolve<IT>();
	}

4) AOP

我們在不期望改變原有類的情況下,在方法執行時嵌入一下邏輯,
讓我們可以在方法執行的切面上任意插入我們的邏輯。

	#region Class
	public class MyInterceptor : IInterceptor
    {
        public void Intercept(IInvocation invocation)
        {
            Console.WriteLine($"intercept before");i
            // 不執行該語句 就不執行原有的方法
            invocation.Proceed();
            Console.WriteLine($"intercept after");
        }
    }

	public class T: IT
    {
        public void Write()
        {
            Console.WriteLine($"T_Write()");
        }
    }
	#endregion

    public void ConfigureContainer(ContainerBuilder builder)
    {
        #region AOP
        // 註冊攔截器到容器中
        builder.RegisterType<MyInterceptor>();
        // builder.RegisterType<MyNameService>();
        builder.RegisterType<T>().As<IT>()
            // 允許屬性註冊
            .PropertiesAutowired()
            // 指定攔截器
            .InterceptedBy(typeof(MyInterceptor))
            // 實現接口攔截器
            // (如果用類攔截器,方法需要設置爲虛方法,允許繼承類重載的情況下,纔可以攔截到具體方法)
            .EnableInterfaceInterceptors();
        #endregion
    }

使用時

	public void Configure(IApplicationBuilder app)
	{
		// 也可以直接在構造函數中請求 ILifetimeScope  
		ILifetimeScope AutofacContainer = app.ApplicationServices.GetAutofacRoot();
		// t 的 Attr 屬性不爲 null
		IT t = AutofacContainer.Resolve<IT>();
		// 可以看到方法的執行順序
		t.Write();
	}

4) 子容器

Autofac 具備給子容器命名的特性,

    public void ConfigureContainer(ContainerBuilder builder)
    {
        #region 子容器
        builder.RegisterType<T>()
            // 將一個服務注入到特定命名爲 myscope 的子容器中
            .InstancePerMatchingLifetimeScope("myscope");
        #endregion
    }

使用時

	public void Configure(IApplicationBuilder app)
	{
		// 也可以直接在構造函數中請求 ILifetimeScope  
		ILifetimeScope AutofacContainer = app.ApplicationServices.GetAutofacRoot();
        #region 子容器
        // 創建了一個叫 myscope 的子容器
        using (var myscope = AutofacContainer.BeginLifetimeScope("myscope"))
        {
            var t1= myscope.Resolve<T>();
            using (var scope = myscope.BeginLifetimeScope())
            {
                var t2= scope.Resolve<T>();
                var t3= scope.Resolve<T>();
                // 得到的都是同一個對象
                var co1 = t1 == t2; // true
                var co2 = t1 == t3; // true
            }
        }
        #endregion
	}

當我們不期望對象在根容器創建時,
又希望它在某一定範圍內是單例模式的情況下,可以使用 子容器。


參考文檔

07 | 用Autofac增強容器能力 - 肖偉宇

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章