.net core的依賴注入
【摘要】
vs新建的.net core專案內建了依賴注入功能,本文簡單地展示如何使用core的依賴注入,以及使用IOC容器來替換core自帶的依賴注入容器。
【正文】
1.使用core專案的依賴注入
新建.net core5 webapi專案,建立MathBook.cs、EnglishBook.cs、Ibook.cs檔案
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace app { public class MathBook: Ibook {public string read() { return "看數學書"; } } }
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace app { public class EnglishBook: Ibook { public string read() { return "看英語書"; } } }
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace app { public interface Ibook { string read(); } }
在Startup.cs中嘗試註冊例項和呼叫例項。
namespace app { public class Startup { public void ConfigureServices(IServiceCollection services) {
//IserviceCollection提供註冊 services.AddSingleton<Ibook, MathBook>(); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
//IServiceProvider提供例項 app.ApplicationServices獲取
var provider = app.ApplicationServices; //// 輸出 var result = provider.GetService<Ibook>(); Console.WriteLine(result.read()); } } }
執行程式,獲得結果。
ServiceCollection註冊例項有三個方法,AddTransient、AddSingleton、AddScoped。對應提供的例項會有不同生命週期。
Transient,每次呼叫GetServie方法都會建立一個新的例項。
Singleton,整個程式執行期間只建立一個例項。
Scoped,在同一個scope中,只建立一個例項。在一次http請求的整個過程中,預設共用一個scope。
.net core專案已經作了依賴注入的實現,直接使用便可。
public void ConfigureServices(IServiceCollection services) { services.AddSingleton<Ibook, MathBook>();//註冊例項 services.AddControllers();//使用api }
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace app.Controllers { [Route("api/[controller]")] [ApiController] public class BookController : ControllerBase { private Ibook _book; public BookController(Ibook book) { this._book = book; } [Route("toRead")] [HttpGet] public string toRead() { return _book.read(); } } }
呼叫介面,可以獲得結果。
2.替換成其它ioc容器
原有的依賴注入容器在面對大型專案會有些麻煩,原因是隻能一個個進行註冊例項,有可能光是引用名稱空間就佔了幾百行。
這裡使用unity進行替換。
安裝unity相關包
在Program.cs中新增UseUnityServiceProvider()。
using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Unity; using Unity.Microsoft.DependencyInjection; namespace app { public class Program { public static void Main(string[] args) { CreateHostBuilder(args).Build().Run(); } public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .UseUnityServiceProvider() .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); }); } }
在Startup.cs中新增ConfigureContainer方法,用來處理untiy容器,往容器中註冊例項。
public void ConfigureContainer(IUnityContainer container) { ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap(); fileMap.ExeConfigFilename = Path.Combine(AppDomain.CurrentDomain.BaseDirectory + "unity.config"); Configuration configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None); UnityConfigurationSection section = (UnityConfigurationSection)configuration.GetSection(UnityConfigurationSection.SectionName); section.Configure(container, "gContainer");//寫法一:給容器載入在配置檔案中name為“gContainer”的<container> }
上面是unity通過讀取配置檔案去註冊,unity.config屬性需要設定為始終複製。
<configuration> <configSections> <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Unity.Configuration"/> </configSections> <unity> <assembly name="app"/> <containers> <container name="gContainer"> <register type="app.Ibook" mapTo="app.MathBook" /> </container> </containers> </unity> </configuration>
Unity.Microsoft.DependencyInjection這個包的作用就是將unity的註冊例項行為轉化到.net core依賴容器,生成例項的生命週期是由.net core內建的容器進行管理。閱讀該包原始碼可以瞭解。
using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Hosting; namespace Unity.Microsoft.DependencyInjection { public static class HostingExtension { private static ServiceProviderFactory _factory; public static IHostBuilder UseUnityServiceProvider(this IHostBuilder hostBuilder, IUnityContainer container = null) { _factory = new ServiceProviderFactory(container); return hostBuilder.UseServiceProviderFactory<IUnityContainer>(_factory) .ConfigureServices((context, services) => { services.Replace(ServiceDescriptor.Singleton<IServiceProviderFactory<IUnityContainer>>(_factory)); services.Replace(ServiceDescriptor.Singleton<IServiceProviderFactory<IServiceCollection>>(_factory)); }); } public static IWebHostBuilder UseUnityServiceProvider(this IWebHostBuilder hostBuilder, IUnityContainer container = null) { _factory = new ServiceProviderFactory(container); #if NETCOREAPP1_1 return hostBuilder.ConfigureServices((services) => { services.Replace(ServiceDescriptor.Singleton<IServiceProviderFactory<IUnityContainer>>(_factory)); services.Replace(ServiceDescriptor.Singleton<IServiceProviderFactory<IServiceCollection>>(_factory)); }); #else return hostBuilder.ConfigureServices((context, services) => { services.Replace(ServiceDescriptor.Singleton<IServiceProviderFactory<IUnityContainer>>(_factory)); services.Replace(ServiceDescriptor.Singleton<IServiceProviderFactory<IServiceCollection>>(_factory)); }); #endif } } }
大致就是,使用了UseUnityServiceProvider的拓展方法後,unity的註冊例項,除了在unity自己的容器操作,同時還會對.net core的serviceCollection容器進行操作。
執行程式,呼叫api/Book/toRead介面,成功得到結果。
至此,成功使用unity去替換實現.net core中原有的依賴注入。