NET Core3.1 用 Autofac 实现IOC容器
一、IOC容器
IOC(Inversion of Control,控制反转),他不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合,更优良的程序。
DI(依赖注入)。IOC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI(Dependency Injection,依赖注入)来实现的。
使用依赖注入,有以下优点:
- 传统的代码,每个对象负责管理与自己需要依赖的对象,导致如果需要切换依赖对象的实现类时,需要修改多处地方。同时,过度耦合也使得对象难以进行单元测试。
- 依赖注入把对象的创造交给外部去管理,很好的解决了代码紧耦合(tight couple)的问题,是一种让代码实现松耦合(loose couple)的机制。
- 松耦合让代码更具灵活性,能更好地应对需求变动,以及方便单元测试。
二、使用——Autofac
1、引入nuget包
在Nuget中引入两个:Autofac.Extras.DynamicProxy(Autofac的动态代理,它依赖Autofac,所以可以不用单独引入Autofac)、Autofac.Extensions.DependencyInjection(Autofac的扩展)
2、在Startup类下面创建 ConfigureContainer 方法
Core 3.0以上版本写法请注意和2.2是不一样的,完整代码如下
public void ConfigureContainer(ContainerBuilder builder) { var basePath = Microsoft.DotNet.PlatformAbstractions.ApplicationEnvironment.ApplicationBasePath; //注册要通过反射创建的组件 //builder.RegisterType<AdvertisementServices>().As<IAdvertisementServices>(); #region 带有接口层的服务注入
//项目引用接口, 服务层和仓储层的bin文件直接使用,实现解耦 var servicesDllFile = Path.Combine(basePath, "Blog.Core.Services.dll"); var repositoryDllFile = Path.Combine(basePath, "Blog.Core.Repository.dll"); if (!(File.Exists(servicesDllFile) && File.Exists(repositoryDllFile))) { throw new Exception("Repository.dll和service.dll 丢失,因为项目解耦了,所以需要先F6编译,再F5运行,请检查 bin 文件夹,并拷贝。"); } // AOP 开关,如果想要打开指定的功能,只需要在 appsettigns.json 对应对应 true 就行。 var cacheType = new List<Type>();// 获取 Service.dll 程序集服务,并注册 var assemblysServices = Assembly.LoadFrom(servicesDllFile); builder.RegisterAssemblyTypes(assemblysServices) .AsImplementedInterfaces() .InstancePerDependency() .EnableInterfaceInterceptors()//引用Autofac.Extras.DynamicProxy; .InterceptedBy(cacheType.ToArray());//允许将拦截器服务的列表分配给注册。 // 获取 Repository.dll 程序集服务,并注册 var assemblysRepository = Assembly.LoadFrom(repositoryDllFile); builder.RegisterAssemblyTypes(assemblysRepository) .AsImplementedInterfaces() .InstancePerDependency(); #endregion #region 没有接口层的服务层注入 //因为没有接口层,所以不能实现解耦,只能用 Load 方法。 //注意如果使用没有接口的服务,并想对其使用 AOP 拦截,就必须设置为虚方法 //var assemblysServicesNoInterfaces = Assembly.Load("Blog.Core.Services"); //builder.RegisterAssemblyTypes(assemblysServicesNoInterfaces); #endregion #region 没有接口的单独类 class 注入 //只能注入该类中的虚方法 builder.RegisterAssemblyTypes(Assembly.GetAssembly(typeof(Love))) .EnableClassInterceptors() .InterceptedBy(cacheType.ToArray()); #endregion }
没有接口的单独类 class 注入,只能注入该类中的虚方法
/// <summary> /// 这是爱 /// </summary> public class Love {
//虚方法 public virtual string SayLoveU() { return "I ♥ U"; } }
3、在program类下面build
public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .UseServiceProviderFactory(new AutofacServiceProviderFactory()) //<--NOTE THIS .ConfigureWebHostDefaults(webBuilder => { webBuilder .UseStartup<Startup>() .UseUrls("http://localhost:8081") .ConfigureLogging((hostingContext, builder) => { builder.ClearProviders(); builder.SetMinimumLevel(LogLevel.Trace); builder.AddConfiguration(hostingContext.Configuration.GetSection("Logging")); builder.AddConsole(); builder.AddDebug(); }); });
4、构造函数方式来注入
依赖注入有三种方式(构造方法注入、setter方法注入和接口方式注入),我们平时基本都是使用其中的构造函数方式实现注入,
//接口
readonly IAdvertisementServices _advertisementServices; /// <summary> /// 构造函数 /// </summary> /// <param name="advertisementServices"></param> public BlogController(IAdvertisementServices advertisementServices) { _advertisementServices = advertisementServices; } [HttpGet("{id}", Name = "Get")] public async Task<List<Advertisement>> Get(int id) { //IAdvertisementServices advertisementServices = new AdvertisementServices(); //不依赖注入是需要引用两个命名空间IServices层和Services层 //使用 return await _advertisementServices.Query(d => d.Id == id); }
5、程序集注入 —— 实现层级引用的解耦
这是一个学习的思路,大家要多想想,可能会感觉无聊或者没用,但是对理解项目启动和加载,还是很有必要的。
1、项目最终只依赖抽象
最终的效果是这样的:工程只依赖抽象,把两个实现层删掉,引用这两个接口层。
2、配置仓储和服务层的程序集输出
将 Blog.Repository 层和 Service 层项目生成地址改成相对路径,这样大家就不用手动拷贝这两个 dll 了,F6编译的时候就直接生成到了 api 层 bin 下了:
“..\Blog.Core\bin\Debug\”
好了,项目启动就可以运行了
常见错误:
经常会遇到一个错误:None of the constructors found with ........,
查看你的service服务,是不是用了其他的仓储repository,但是又缺少了构造函数。
参考自:https://www.cnblogs.com/laozhang-is-phi/p/9541414.html
如果写的不明白可以看看大神原文,我是在跟着大神学习的,下节学习AOP切面