前言
Autofac是.NET领域最为流行的IOC框架之一,传说是速度最快的一个。它和C#语言的结合非常紧密,在使用过程中对你的应用的侵入性几乎为零,更容易与第三方的组件集成。主要优点如下(此段描述为转载):
它是C#语言联系很紧密,也就是说C#里的很多编程方式都可以为Autofac使用,例如可以用Lambda表达式注册组件
较低的学习曲线,学习它非常的简单,只要你理解了IoC和DI的概念以及在何时需要使用它们
XML配置支持
自动装配
微软的Orchad开源程序使用的就是Autofac,从该源码可以看出它的方便和强大
准备工作
本文演示的项目时基于.Net Core3.1的,同时需要通过nuget下载安装以下几个dll文件:
基本用法
在前面的《基于Dapper的扩展方法来简单封装WebApi接口》一文中介绍过在.net core2.1中使用Autofac的方式,在ASP.NET Core 1.1 - 2.2 中, 你可以调用 WebHostBuilder 的 services.AddAutofac().。但这不适用于ASP.NET Core 3+ 或 .NET Core 3+ ,在 ASP.NET Core 3+ 需要你直接指定一个service provider factory而不是把它加入进service collection。基本用法的代码如下:
首先修改Progam中的CreateHostBuilder方法,使用autofac的容器工厂替换系统默认的容器:
public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .UseServiceProviderFactory(new AutofacServiceProviderFactory())//使用autofac的容器工厂替换系统默认的容器 .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); });
然后在Startup中增加一个方法ConfigureContainer
public void ConfigureContainer(ContainerBuilder containerBuilder) { //指定服务的注册 containerBuilder.RegisterType<UserService>().As<IUserService>().InstancePerLifetimeScope().AsImplementedInterfaces(); containerBuilder.RegisterType<ProductService>().As<IProductService>().InstancePerLifetimeScope().AsImplementedInterfaces(); var container = containerBuilder.Build(); IUserService userService = container.Resolve<IUserService>(); IProductService productService = container.Resolve<IProductService>(); userService.Show(); productService.Show(); }
调试运行,我们发现ConfigureContainer的方法并没有被引用,但已经可以进到这个方法里并实现的服务的注册,这就是因为我们指定了UseServiceProviderFactory:
Autofac生命周期
简单介绍,详情请见参考资料:
//1、瞬时生命周期:注册之后,每次获取到的服务实例都不一样(默认的注册方式) containerBuilder.RegisterType<UserService>().As<IUserService>().InstancePerDependency(); //2、单例生命周期:整个容器中获取的服务实例都是同一个 containerBuilder.RegisterType<UserService>().As<IUserService>().SingleInstance(); //3、作用域生命周期:在相同作用域下获取到的服务实例是相同的 containerBuilder.RegisterType<UserService>().As<IUserService>().InstancePerLifetimeScope(); //4、作用域生命周期:可以指定到某一个作用域,然后在相同作用域下共享服务实例 containerBuilder.RegisterType<UserService>().As<IUserService>().InstancePerMatchingLifetimeScope("My"); //5、http请求上下文的生命周期:在一次Http请求上下文中,共享一个组件实例。仅适用于asp.net mvc开发。 containerBuilder.RegisterType<UserService>().As<IUserService>().InstancePerRequest(); //6、拥有隐式关系类型的创建新的嵌套生命周期的作用域,在一个生命周期域中所拥有的实例创建的生命周期中, // 每一个依赖组件或调用Resolve()方法创建一个单一的共享的实例,并且子生命周期域共享父生命周期域中的实例 containerBuilder.RegisterType<UserService>().InstancePerOwned<IUserService>();
实际项目中用法
上面的代码只是简单演示了下Autofac如何注册服务实例的,在实际使用时我们不会这样去写代码,每增加一个接口及其实现,都需要手动注册一下,这样的耦合度太高。因此我们需要进行代码优化,下面将使用反射的方式来实现服务的注册,改造上面的代码:
public void ConfigureContainer(ContainerBuilder containerBuilder) { Assembly service = Assembly.Load("AspNetCore.Ioc.Service"); Assembly iservice = Assembly.Load("AspNetCore.Ioc.Interface"); containerBuilder.RegisterAssemblyTypes(service, iservice) .Where(t => t.FullName.EndsWith("Service") && !t.IsAbstract) //类名以service结尾,且类型不能是抽象的 .InstancePerLifetimeScope() //生命周期,, .AsImplementedInterfaces() .PropertiesAutowired(); //属性注入 }
运行结果如下
一个接口多个实现的服务注册
在实际应用中有这样一种场景,比如IUserService接口被多个类继承并实现,那么此时应该如何注册服务并实现调用呢?
其实上面Startup中的注册方式就已经满足服务的注册,只是需要在相应的Controller调用的地方修改即可,如:
public class UserController : Controller { /// <summary> /// IUserService服务实现的集合 /// </summary> private readonly IEnumerable<IUserService> _userServices = null; public UserController(IEnumerable<IUserService> userServices) { _userServices = userServices; } public IActionResult Index() { foreach (var item in _userServices) { item.Show(); } return View(); } }
调试运行的结果如下
值得说明的是,一个接口有多个实现的情况,在注册服务的时候,可以选择一些策略来实现只注册其中某几个实例,示例代码如下:
//一个接口有多个实现:注册所有实现的服务实例 builder.RegisterAssemblyTypes(Assembly.Load("AspNetCore.Ioc.Service")).As<IUserService>(); //一个接口有多个实现:只注册以A结尾的服务实例 builder.RegisterAssemblyTypes(Assembly.Load("AspNetCore.Ioc.Service")).Where(c=>c.Name.EndsWith("A")).As<IUserService>(); //一个接口有多个实现:注册所有实现的服务实例,并排除UserServiceA服务实例 builder.RegisterAssemblyTypes(Assembly.Load("AspNetCore.Ioc.Service")).Except<UserServiceA>().As<IUserService>();
隔离服务注册的逻辑代码
为了简化Startup中的代码,还可以自定义一个MyAutofacModule的方式,将服务注册的代码抽离出来,放到单独的文件中。这时我们就需要新建一个MyAutofacModule类,并继承Autofac.Module,同时重写其中的Load方法,具体代码如下:
using System.Linq; using System.Reflection; using Autofac; using Autofac.Configuration; using Microsoft.Extensions.Configuration; namespace AspNetCore.Ioc.Web.Utility { public class MyAutofacModule : Autofac.Module { protected override void Load(ContainerBuilder builder) { //反射程序集方式服务注册 Assembly service = Assembly.Load("AspNetCore.Ioc.Service"); Assembly iservice = Assembly.Load("AspNetCore.Ioc.Interface"); builder.RegisterAssemblyTypes(service, iservice) .Where(t => t.FullName.EndsWith("Service") && !t.IsAbstract) //类名以service结尾,且类型不能是抽象的 .InstancePerLifetimeScope() //作用域生命周期 .AsImplementedInterfaces() .PropertiesAutowired(); //属性注入 } } }
重写Load方法中的逻辑其实就是将原本 写在Startup中的注册代码迁移到MyAutofacModule中,然后将Startup中的ConfigureContainer方法修改成如下:
public void ConfigureContainer(ContainerBuilder containerBuilder) { containerBuilder.RegisterModule<MyAutofacModule>(); }
具体运行结果这里就不展示,和前面的一样。
配置文件的方式服务注册
为了让注册服务的方式更灵活,我们还可以通过配置文件的方式来实现,将所有的程序集信息放到配置文件中,这样便于后期的程序扩展。那么首先来看下配置文件应该如何写:
需要注意的是要将autofac.json文件的属性改成始终复制
autofac.json文件:
{ "defaultAssembly": "AspNetCore.Ioc.Interface", //接口所在的程序集名称 "components": [ { "type": "AspNetCore.Ioc.Service.UserService,AspNetCore.Ioc.Service", //接口的实现 全名称 "services": [ { "type": "AspNetCore.Ioc.Interface.IUserService" // 接口的全名称 } ], "instanceScope": "single-instance", //单例生命周期 "injectProperties": true //是否支持属性注入 }, { "type": "AspNetCore.Ioc.Service.ProductService,AspNetCore.Ioc.Service", //接口的实现 全名称 "services": [ { "type": "AspNetCore.Ioc.Interface.IProductService" // 接口的全名称 } ], "instanceScope": "single-instance", //单例生命周期 "injectProperties": true //是否支持属性注入 } ] }
修改MyAutofacModule中的调用方法:
protected override void Load(ContainerBuilder builder) { //Autofac 基于配置文件的服务注册 IConfigurationBuilder configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddJsonFile("Config/autofac.json"); IConfigurationRoot root = configurationBuilder.Build(); //开始读取配置文件中的内容 ConfigurationModule module = new ConfigurationModule(root); //根据配置文件的内容注册服务 builder.RegisterModule(module); }
运行结果如下
总结
到这里基本就完成了.net core3.1下使用Autofac的基本用法,当然还有其他的一些用法,比如Autofac中实现AOP等,本文暂时就到这里了。参考资料:
评论列表
评论内容: