天天看点

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 框架使用实战,依赖注入

执行后

继续阅读