.NET Core使用第三方IOC容器Autofac

731 阅读5分钟

序言

.NET Core WebApi开发 - 掘金 (juejin.cn) 上篇文章介绍了.NET Core自带的IOC容器,可以实现多重方式的DI依赖注入,但是,随着业务的增多,Repository和Service层文件的增加,我们不可能去每个文件都单独的添加到容器中,那我们怎么去实现批量注入呢,所以就有了第三方的IOC容器Autofac,Autofac的功能非常强大,但是我们不一定全部都用得到,此篇文章只说明批量注入的使用,感兴趣的可以去官网了解下。Autofac

Autofac方法介绍

此处有借鉴:AutoFac中常用方法说明 - bxzjzg - 博客园 (cnblogs.com)

这里稍微介绍下Autofac的api

AsImplementedInterfaces
是以接口方式进行注入,注入这些类的所有的公共接口作为服务

InstancePerRequest
每次请求共享同一个实例,使用ef时,使不同的操作使用同一个数据上下文

PreserveExistingDefaults
如果不止一个组件暴露了相同的服务, Autofac将使用最后注册的组件作为服务的提供方,那么使用PreserveExistingDefaults防止后面注册的覆盖前面注册的

builder.Register<A>().As<IA>();

builder.Register<AA>().As<IA>();  //这样AA就会覆盖A

builder.Register<AA>().As<IA>().PreserveExistingDefaults();   //这样A是IA的默认值,不会被AA覆盖

InstancePerDependency
为每个依赖或者调用(Resolve())都创建一个新的对象,唯一的实例

SingleInstance
为每次请求都使用同一个对象,单例模式.告诉容器,是个单例,但这个单例不用自己实现。

RegisterGeneric
对泛型类进行注册

builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IRepository<>));

RegisterInstance
注册一个实例,比如: 对已经存在的对象的实例进行注册,这样可以使实例转化为由容器托管的实例

RegisterControllers
注册Controllers

builder.RegisterControllers(Assembly.GetExecutingAssembly());  //注册当前程序集中的所有Controllers

RegisterFilterProvider
注册Filter过滤器

builder.RegisterFilterProvider()

RegisterAssemblyTypes
注册类

builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly());//注册了当前程序集内的所有的类

InstancePerMatchingLifetimeScope
在一个做标识的生命周期域中,每一个依赖或调用创建一个单一的共享的实例。打了标识了的生命周期域中的子标识域中可以共享父级域中的实例

builder.RegisterType<A>().AsImplementedInterfaces().InstancePerMatchingLifetimeScope();

InstancePerHttpRequest
在一次Http请求上下文中,共享一个组件实例。仅适用于asp.net mvc开发

InstancePerLifetimeScope
在一个生命周期中,每一次的依赖组件或调用(Resolve())创建一个单一的共享的实例,且每一个不同的生命周期域,实例是不同的

UsingConstructor
自动装配,手动使用特定的构造函数

builder.RegisterType<A>().UsingConstructor(typeof(IA),typeof(IAA));   //这样指定调用的是A(IA,IAA)的构造函数,如果该构造函数不存在则报错

PropertiesAutowired
配置组件,以便其类型在容器中注册的任何属性都将连接到相应服务的实例。

EnableInterfaceInterceptors 在目标类型上启用接口拦截。侦听器将通过类或接口上的 Intercept 属性确定,或者使用 InterceptedBy()调用添加。

InterceptedBy
允许将侦听器服务列表分配给注册。

[AsClosedTypesOf(open)]AsClosedTypesOf(typeof(IA<>)):
如果是多继承的话,注册的程序集所对应的服务(接口)是离这个类最近的开放的泛型实例(接口)

builder.RegisterAssemblyTypes(A).AsClosedTypesOf(typeof(IRepository<>));

如何实现批量注入

一步一步来,我们先创建Repository和Service的类库

image.png

代码也很简单的写了个测试的方法

    public interface ITestRepository
    {
        public string GetTestData();
    }
    public class TestRepository : ITestRepository
    {
        public string GetTestData()
        {
            return "Class TestRepository Method GetTestData:测试数据";
        }
    }
    public interface ITestService
    {
        public string Test(string str);
    }
    public class TestService : ITestService
    {
        ITestRepository testRepository;
        public TestService(ITestRepository _testRepository)
        {
            testRepository= _testRepository;
        }
        public string Test(string str)
        {
            string data=testRepository.GetTestData();
            return $"Class TestService Method Test Paramter:{str} TestData:{data}";
        }
    }

好的,我们看到这里TestServer的构造函数中我们也注入了Repository,那这种架构方式该如何注入到容器中呢? 我们先看下用.NET Core自带的IOC容器如何操作的。

    services.AddScoped<ITestRepository, TestRepository>();
    services.AddScoped<ITestService, TestService>();

现在看着只有一个Service和一个Repository文件的情况下我们都需要单独的注入,那当我们业务复杂起来后肯定也会越来越多,那肯定是不便于开发的。而Autofac则能够实现批量注入,那我们来看下应该如何实现呢?

首先我们需要引入Autofac对应的包

<PackageReference Include="Autofac" Version="6.3.0" />
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="7.2.0" />
<PackageReference Include="Autofac.Extras.DynamicProxy" Version="6.0.1" />

创建一个注册的类,并且继承Autofac.Module,重写Load方法,下面代码能够看到是可以通过dll(动态链接库)文件来实现注入的,那就是说,项目可以不引用这两个项目,只用把生成的dll放到指定的位置去读取就行了,能够实现项目之间的解耦。

    /// <summary>
    /// 注入Service和Repository
    /// </summary>
    public class AutofacRegister : Autofac.Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            var basePath = AppContext.BaseDirectory;

            var servicesDllFile = Path.Combine(basePath, "Service.dll");
            var repositoryDllFile = Path.Combine(basePath, "Repository.dll");

            // 获取 Service.dll 程序集服务,并注册
            var assemblysServices = Assembly.LoadFrom(servicesDllFile);
            builder.RegisterAssemblyTypes(assemblysServices)
                      .AsImplementedInterfaces()
                      .InstancePerDependency()
                      .PropertiesAutowired();

            // 获取 Repository.dll 程序集服务,并注册
            var assemblysRepository = Assembly.LoadFrom(repositoryDllFile);
            builder.RegisterAssemblyTypes(assemblysRepository)
                   .AsImplementedInterfaces()
                   .InstancePerDependency()
                   .PropertiesAutowired();
        }
    }

下一步就是在Program里面启用Autofac工厂

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
         .UseServiceProviderFactory(new AutofacServiceProviderFactory())//启用Autofac工厂
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
                }).UseNLog();//启用NLog

在Startup文件中调用我们刚才写的AutofacRegister,直接新增一个ConfigureContainer方法

        public void ConfigureContainer(ContainerBuilder builder)
        {
            builder.RegisterModule(new AutofacRegister());
        }

好了,我们在控制器中调用Service方法看看效果。

    [Route("api/[controller]")]
    [ApiController]
    public class TestController : ControllerBase
    {
        private readonly ITestService _testServer;

        public TestController(ITestService testServer)
        {
            _testServer = testServer;
        }
        [HttpGet("TestService")]
        public string TestService(string str)
        {
            return _testServer.Test(str);
        }
    }

image.png 完美运行,看到返回结果也是正常的。

总结

Autofac功能比较强大,还有很多功能没有用到,在日常开发中,如果没有多层的架构或者没有面向接口开发也用不到,但是多层架构也有诸多的好处。具体使用场景请斟酌使用。