一、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切面