天天看點

Asp.Net Core Autofac 架構使用實戰,依賴注入

作者:CShap新勢力

Autofac 是一個輕量級的依賴注入(DI)架構,它可以幫助 .NET 開發人員更好地管理對象的生命周期和依賴關系。Autofac 可以大大簡化應用程式中對象之間的耦合,使代碼更加可維護和可測試。

Autofac 的主要特點:

輕量級:Autofac 是一個非常輕量級的架構,其核心庫隻有幾個 DLL 檔案。這意味着它可以很容易地與其他架構內建,并且對應用程式的性能沒有任何影響。

靈活性:Autofac 提供了多種不同的注冊方式,如 XML 配置檔案、代碼配置和屬性注解等。開發人員可以根據自己的需求選擇最适合的注冊方式。

高性能:由于 Autofac 是一個輕量級架構,它的性能非常高。在執行個體化對象時,Autofac 可以比其他 DI 架構更快地找到并建立所需的依賴項。

生命周期管理:Autofac 提供了多種不同的生命周期管理選項,如瞬态(Transient)、作用域(Scoped)和單例(Singleton)等。這使得開發人員可以更好地控制對象的生命周期,有效地降低記憶體使用和提高性能。

AOP 支援:Autofac 可以輕松地與 AOP 架構內建,如 Castle DynamicProxy 等。這使得開發人員可以很容易地實作諸如事務管理、緩存和驗證等方面的橫切關注點。

Autofac 其核心思想是将對象之間的依賴關系從應用程式中分離出來。相對于傳統的執行個體化對象方式,DI 可以有效地降低代碼的耦合度,提高可維護性和可測試性。

Autofac 的架構主要分為兩個部分:注冊器(ContainerBuilder)和容器(IContainer)。注冊器用于注冊應用程式中所有需要注入的服務群組件,而容器則用于建立和管理這些元件執行個體。

注入方式

先準備點代碼

接口層代碼:粘貼太麻煩了,直接貼圖,代碼也很簡單

Asp.Net Core Autofac 架構使用實戰,依賴注入

貓的接口

Asp.Net Core Autofac 架構使用實戰,依賴注入

狗的接口

Asp.Net Core Autofac 架構使用實戰,依賴注入

豬的接口

Asp.Net Core Autofac 架構使用實戰,依賴注入

技能接口

Asp.Net Core Autofac 架構使用實戰,依賴注入

人的接口

實作層代碼:粘貼太麻煩了,直接貼圖,代碼也很簡單

Asp.Net Core Autofac 架構使用實戰,依賴注入

貓的實作

Asp.Net Core Autofac 架構使用實戰,依賴注入

狗的實作

Asp.Net Core Autofac 架構使用實戰,依賴注入

豬的實作

Asp.Net Core Autofac 架構使用實戰,依賴注入

人的實作

上面代碼準備好了。我們搞注入

構造函數注入 (預設的注入方式)

{
                //構造函數注入
                ContainerBuilder builder = new ContainerBuilder();
                builder.RegisterType<DogService>().As<IDogService>();
                builder.RegisterType<PersonService>().As<IPersonService>();
                IContainer container = builder.Build();
                var p = container.Resolve<IPersonService>();
                p.Say();
                p.PlayerWithDog();           
}           

上面代碼通過RegisterType引入IDogService,後引入IPersonService,會執行PersionService中有一個IDogService作為參數的構造函數

{
                ContainerBuilder builder = new ContainerBuilder();
                builder.RegisterType<PigService>().As<IPigService>();
                builder.RegisterType<CatService>().As<ICatService>();
                builder.RegisterType<DogService>().As<IDogService>();
                builder.RegisterType<PersonService>().As<IPersonService>();
                IContainer container = builder.Build();
                var p = container.Resolve<IPersonService>();
                p.Say();
                p.PlayerWithDog();
}           

上面代碼通過RegisterType引入了IDogService、ICatService、IPigService,是以會走具有三個參數的構造函數

{
                ContainerBuilder builder = new ContainerBuilder();
                builder.RegisterType<DogService>().As<IDogService>();
                builder.RegisterType<CatService>().As<ICatService>();
                //通過UsingConstructor方法指定要執行的構造函數
                builder.RegisterType<PersonService>().As<IPersonService>().UsingConstructor(typeof(IDogService), typeof(ICatService));
                IContainer container = builder.Build();
                var p = container.Resolve<IPersonService>();
                p.Say();
                p.PlayerWithDog();
                p.PlayerWithCat();         
}           

上面代碼通過RegisterType引入了IDogService、ICatService、再通過UsingConstructor方法指定要執行的構造函數,是以會走具有二個參數的構造函數

屬性注入

準備代碼,簡單代碼直接貼圖

Asp.Net Core Autofac 架構使用實戰,依賴注入

動物接口

Asp.Net Core Autofac 架構使用實戰,依賴注入

動物實作

{ 
                ContainerBuilder builder = new ContainerBuilder();
                builder.RegisterType<DogService>().As<IDogService>();
                builder.RegisterType<CatService>().As<ICatService>();
                builder.RegisterType<PigService>().As<IPigService>();
                //PropertiesAutowired 使AnimalService執行個體的屬性可以被注入
                builder.RegisterType<AnimalService>().As<IAnimalService>().PropertiesAutowired();
                IContainer container = builder.Build();
                var p = container.Resolve<IAnimalService>();
                p.Say();          
}           

上面代碼 調用PropertiesAutowired 方法,使AnimalService執行個體的屬性可以被注入

屬性注入(屬性注入選擇器)

Asp.Net Core Autofac 架構使用實戰,依賴注入

自定義一個空的特性

Asp.Net Core Autofac 架構使用實戰,依賴注入

自定義屬性選擇器

Asp.Net Core Autofac 架構使用實戰,依賴注入

動物實作2

{
                //屬性注入 屬性注入選擇器
                ContainerBuilder builder = new ContainerBuilder();
                builder.RegisterType<DogService>().As<IDogService>();
                builder.RegisterType<CatService>().As<ICatService>();
                builder.RegisterType<PigService>().As<IPigService>();
                //PropertiesAutowired 使AnimalService2執行個體的屬性可以被注入
                builder.RegisterType<AnimalService2>().As<IAnimalService>().PropertiesAutowired(new IoctAttributeSelector());
                IContainer container = builder.Build();
                var p = container.Resolve<IAnimalService>();
                p.Say();
 }           

上面代碼 調用PropertiesAutowired(new IoctAttributeSelector()) 方法,使AnimalService2執行個體的屬性可以被選擇注入

方法注入

所謂方法注入是指:在接口對象構造後,自動調用對象的某個方法

{
                //方法注入 所謂方法注入是指:在接口對象構造後,自動調用對象的某個方法
                ContainerBuilder builder = new ContainerBuilder();
                builder.RegisterType<DogService>().As<IDogService>();
                builder.RegisterType<CatService>().As<ICatService>();
                builder.RegisterType<PigService>().As<IPigService>();
                builder.RegisterType<AnimalService>().As<IAnimalService>().OnActivated(Activator =>
                {
                    var dogService = Activator.Context.Resolve<IDogService>();
                    var catService = Activator.Context.Resolve<ICatService>();
                    var pigService = Activator.Context.Resolve<IPigService>();
                    Activator.Instance.Play(catService, dogService, pigService);
                });

                IContainer container = builder.Build();
                //建構IAnimalService對象,觸發上述Play方法執行
                var p = container.Resolve<IAnimalService>();          
            }           

上面代碼,構造了AnimalService的執行個體後自動調用Play方法

單抽象多繼承 通過加入key來識别

{
                //單抽象多繼承 通過加入key來識别即可,如下
                ContainerBuilder builder = new ContainerBuilder();
                builder.RegisterType<DogService>().Keyed<ISkillService>("dog");
                builder.RegisterType<CatService>().Keyed<ISkillService>("cat");
                builder.RegisterType<PigService>().Keyed<ISkillService>("pig");
                IContainer container = builder.Build();
                //這裡是狗
                var dog = container.ResolveKeyed<ISkillService>("dog");
                dog.ReleaseSkills();
                //這裡是貓
                var cat = container.ResolveKeyed<ISkillService>("cat");
                cat.ReleaseSkills();
                //這裡是豬
                var pig = container.ResolveKeyed<ISkillService>("pig");
                pig.ReleaseSkills();
}           

上面代碼,貓、狗、豬分别用key辨別來分别

泛型注入

先準備點代碼,貼圖

Asp.Net Core Autofac 架構使用實戰,依賴注入

一個參數的泛型接口

Asp.Net Core Autofac 架構使用實戰,依賴注入

兩個參數的泛型接口

Asp.Net Core Autofac 架構使用實戰,依賴注入

一個參數的實作

Asp.Net Core Autofac 架構使用實戰,依賴注入

兩個參數的實作

{
                //泛型注入
                ContainerBuilder builder = new ContainerBuilder();
                builder.RegisterGeneric(typeof(GenericAService<>)).As(typeof(IGenericAService<>));
                builder.RegisterGeneric(typeof(GenericBService<,>)).As(typeof(IGenericBService<,>));
                IContainer container = builder.Build();
                var p = container.Resolve<IGenericAService<Dog>>();
                var name1 = p.GetFullName();

                var p2 = container.Resolve<IGenericBService<Dog, Cat>>();
                var name2 = p2.GetFullName();        
            }
           

通過程式集加載1

{
                //通過程式集加載
                ContainerBuilder builder = new ContainerBuilder();
                Assembly assemblyInterface = Assembly.LoadFrom("ITestLibraryService.dll");
                Assembly assemblyService = Assembly.LoadFrom("TestLibraryService.dll");
                builder.RegisterAssemblyTypes(assemblyInterface, assemblyService).AsImplementedInterfaces();
                IContainer container = builder.Build();
                //PersonService 會執行參數多的構造函數
                var p = container.Resolve<IPersonService>();
                p.PlayerWithDog(); 
                p.PlayerWithCat();
                p.PlayerWithPig();         
}           

這裡需要說明的是,如果構造函數中有相同參數個數的構造函數,AutoFac會報錯,他就不知道該執行哪個構造函數了。

通過程式集加載2

{
                //通過程式集加載
                ContainerBuilder builder = new ContainerBuilder();
                Assembly iService = Assembly.Load("ITestLibraryService");
                Assembly service = Assembly.Load("TestLibraryService");
                builder.RegisterAssemblyTypes(iService, service).Where(p => p.Name.EndsWith("Service")).AsImplementedInterfaces().InstancePerLifetimeScope();
                IContainer container = builder.Build();
                var p = container.Resolve<IPersonService>();
                p.PlayerWithDog(); 
 							  p.PlayerWithCat(); 
  							p.PlayerWithPig();
}           

上面代碼,規定了名稱必須是Service結尾的

接下來我們聊一聊WebApi中如何使用Autofac 架構

Nuget 先引用幾個包

Asp.Net Core Autofac 架構使用實戰,依賴注入

引用

<ItemGroup>
		<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="8.0.0" />
		<PackageReference Include="Autofac.Extras.DynamicProxy" Version="7.1.0" />
		<PackageReference Include="Castle.Core" Version="5.1.1" />
</ItemGroup>           

Program 添加如下代碼

builder.Services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>());
            builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
            builder.Host.ConfigureContainer<ContainerBuilder>((builder) =>
            {
                var controllersTypesInAssembly = typeof(Program).Assembly.GetExportedTypes()
                    .Where(type => typeof(ControllerBase).IsAssignableFrom(type)).ToArray();
                builder.RegisterTypes(controllersTypesInAssembly).PropertiesAutowired(); //控制器支援屬性注入

                builder.RegisterGeneric(typeof(GenericAService<>)).As(typeof(IGenericAService<>));
                builder.RegisterGeneric(typeof(GenericBService<,>)).As(typeof(IGenericBService<,>));

                Assembly iService = Assembly.Load("ITestLibraryService");
                Assembly service = Assembly.Load("TestLibraryService");
                builder.RegisterAssemblyTypes(iService, service).Where(p => p.Name.EndsWith("Service")).AsImplementedInterfaces().InstancePerLifetimeScope();        
            });           

控制器就可以使用了

Asp.Net Core Autofac 架構使用實戰,依賴注入

控制器代碼

生命周期的管理

InstancePerDependency 瞬時

builder.RegisterType<TestAService>().As<ITestAService>().InstancePerDependency();           

InstancePerLifetimeScope() 範圍

builder.RegisterType<TestBService>().As<ITestBService>().InstancePerLifetimeScope();           

InstancePerMatchingLifetimeScope("name名稱") *比對 name* 聲明周期範圍執行個體

builder.RegisterType<TestDService>().As<ITestDService>().InstancePerMatchingLifetimeScope("myscope");           

SingleInstance 單例

builder.RegisterType<TestCService>().As<ITestCService>().SingleInstance();           

AOP Castle DynamicProxy 動态代理 面向切片程式設計

using Autofac;
using Autofac.Extensions.DependencyInjection;
using Autofac.Extras.DynamicProxy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.Extensions.DependencyInjection.Extensions;

namespace CastleDynamicProxyDemo
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);
            // Add services to the container.
            builder.Services.AddControllers();
            // ...
            builder.Services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>());
            builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
            builder.Host.ConfigureContainer<ContainerBuilder>((builder) =>
            {
                var controllersTypesInAssembly = typeof(Program).Assembly.GetExportedTypes()
                    .Where(type => typeof(ControllerBase).IsAssignableFrom(type)).ToArray();
                builder.RegisterTypes(controllersTypesInAssembly).PropertiesAutowired(); //控制器支援屬性注入
                
                builder.RegisterType<SimpleService>().As<ISimpleService>()
                .EnableInterfaceInterceptors().InterceptedBy(typeof(LogInvocationInterceptor));
                builder.RegisterType<LogInvocationInterceptor>().SingleInstance();        
            });

            var app = builder.Build();
            //.....
            app.Run();
        }
    }
}           

攔截器

using Castle.DynamicProxy;

namespace CastleDynamicProxyDemo
{
    public class LogInvocationInterceptor : IInterceptor
    {
      
        private readonly ILogger<LogInvocationInterceptor> _logger;

        public LogInvocationInterceptor(ILogger<LogInvocationInterceptor> logger)
        {
            _logger = logger;
        }

        public void Intercept(IInvocation invocation)
        {
            PrevProceed(invocation);
            Proceed(invocation);
            AfterProceed(invocation);
        }

        /// <summary>
        /// 繼續執行
        /// </summary>
        /// <param name="invocation"></param>
        protected virtual void Proceed(IInvocation invocation)
        {
            //繼續,這句是真的調用服務的方法
            invocation.Proceed();
        }

        /// <summary>
        /// 執行前
        /// </summary>
        /// <param name="invocation"></param>
        protected virtual void PrevProceed(IInvocation invocation)
        {
            _logger.LogInformation(invocation.Method.Name);
            _logger.LogInformation(#34;{invocation.Arguments}");
        }

        /// <summary>
        /// 執行後
        /// </summary>
        /// <param name="invocation"></param>
        protected virtual void AfterProceed(IInvocation invocation)
        {
            _logger.LogInformation(invocation.ReturnValue.ToString());
        }
    }
}
           
Asp.Net Core Autofac 架構使用實戰,依賴注入

一個簡單的服務

Asp.Net Core Autofac 架構使用實戰,依賴注入

服務的實作

Asp.Net Core Autofac 架構使用實戰,依賴注入

控制器調用

調試下執行過程

Asp.Net Core Autofac 架構使用實戰,依賴注入

執行前

Asp.Net Core Autofac 架構使用實戰,依賴注入

執行

Asp.Net Core Autofac 架構使用實戰,依賴注入

執行後

繼續閱讀