目錄html
軟件設計原則中有一個依賴倒置原則(DIP),爲了更好的解耦,講究要依賴於抽象,不要依賴於具體。而控制反轉(Ioc)就是這樣的原則的其中一個實現思路, 這個思路的其中一種實現方式就是依賴注入(DI)。ide
什麼是依賴:當一個類須要另外一個類協做來完成工做的時候就產生了依賴。函數
什麼是注入: 注入體現的是一個IOC(控制反轉的的思想)。正轉是本身來實例化須要的依賴。反轉是類不該該本身建立它,而是應該由它的調用者傳給它。因而能夠經過構造函數等讓外界把依賴傳給類。測試
爲何要反轉 爲了在業務變化的時候盡少改動代碼可能形成的問題。基於抽象添加新的實現。只須要在原來注入的地方改一下就能夠了。ui
ServiceCollection提供了三種註冊方法分別對應着三種實例生命週期。this
作一個簡單測試:.net
建立測試類:設計
public interface ITest { Guid Guid { get; } } public class Test : ITest { public Guid Guid { get; } public Test() { Guid = Guid.NewGuid(); } }
在ConfigureServices裏註冊3d
public void ConfigureServices(IServiceCollection services) { services.AddTransient<ITest, Test>(); }
經過三種方法來獲取這個Test類實例, Controller和View中代碼以下code
public class HomeController : Controller { private ITest _test; private ILogger<HomeController> _logger; public HomeController(ITest test, ILogger<HomeController> logger) { this._test = test; this._logger = logger; } public IActionResult Index() { //經過構造函數獲取 var res1 = this._test; ViewBag.TestFromConstructor = res1; //經過HttpContext獲取 var res2 = HttpContext.RequestServices.GetService<ITest>(); ViewBag.TestFromContext = res2; return View(); } }
@inject ITest TestFromView <ul> <li>@ViewBag.TestFromConstructor.Guid</li> <li>@ViewBag.TestFromContext.Guid</li> <li>@TestFromView.Guid</li> </ul>
運行,結果以下
03d437d6-2f18-452e-a7fd-ce62cea90381 08b31487-b02b-4d62-bc2b-6d2026389f0c 21a7fc13-6e7b-4590-910b-68d21a7a03d1
說明三種方式獲取了三個不一樣的實例, 刷新一下頁面, 又變成了另外三個不一樣的值.
如今在startup文件中將原來的 services.AddTransient<ITest,Test>() 改成 services.AddScoped<ITest,Test>() , 其餘不變, 從新運行一下, 結果以下
050fef7e-2dc3-4d7d-8733-683b54b40b0b 050fef7e-2dc3-4d7d-8733-683b54b40b0b 050fef7e-2dc3-4d7d-8733-683b54b40b0b
刷新一下:
c9e5df8d-b085-4e3a-b883-fa083ba1d136 c9e5df8d-b085-4e3a-b883-fa083ba1d136 c9e5df8d-b085-4e3a-b883-fa083ba1d136
三組數字相同, 刷新一下, 又變成了另外三組同樣的值, 這說明在同一次請求裏, 獲取的實例是同一個。
最經常使用的DBContext默認構建爲Scope實例。即能減小實例初始化的消耗,還能實現跨Service事務的功能。
再將 services.AddScoped<ITest,Test>() 改成 services.AddSingleton<ITest,Test>() , 從新運行, 此次結果是
42ef5162-5781-427b-ac9d-a152500ed32f 42ef5162-5781-427b-ac9d-a152500ed32f 42ef5162-5781-427b-ac9d-a152500ed32f
發現三組值是同樣的, 說明得到的是同一個實例, 在刷新一下頁面, 仍然是這三組值, 說明屢次請求得到的結果也是同一個實例.
在Startup類ConfigureService中初始化
public void ConfigureServices(IServiceCollection services) { services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); services.AddTransient<ITest, Test>(); }
方法中默認調用了services.AddMvc(), 是IServiceCollection的一個擴展方法 public static IMvcBuilder AddMvc(this IServiceCollection services), 做用就是向這個清單中添加了一些MVC須要的服務,例如Authorization、RazorViewEngin、DataAnnotations等。
Controller中使用
private ITest _test; private ILogger<HomeController> _logger; public HomeController(ITest test, ILogger<HomeController> logger) { this._test = test; this._logger = logger; }
經過HttpContext來獲取實例
HttpContext下有一個RequestedService一樣能夠用來獲取實例對象,不過這種方法通常不推薦。須要添加Microsoft.Extension.DependencyInjection的using來調用這個方法的。
HttpContext.RequestServices.GetService<ITest>()
View中使用
在View中經過@inject聲明
@inject ITest TestFromView <ul> <li>@TestFromView.Guid</li> </ul>
對於每次請求, 咱們最初配置的根IServiceProvider經過CreateScope()建立了一個新的IServiceScope, 而這個IServiceScope的ServiceProvider屬性將負責本次該次請求的服務提供, 當請求結束, 這個ServiceProvider的dispose會被調用。
在2.0中, ServiceProvider只調用由它建立的 IDisposable 類型的 Dispose。 若是將一個實例添加到容器,它將不會被釋放。
例如:
services.AddSingleton<ITest>(new Test());
能夠將默認的容器改成其餘的容器, 好比Autofac, 須要把Startup類裏面的 ConfigureService的 返回值從 void改成 IServiceProvider便可。而返回的則是一個AutofacServiceProvider。
public IServiceProvider ConfigureServices(IServiceCollection services) { services.AddMvc(); // Add other framework services // Add Autofac var containerBuilder = new ContainerBuilder(); containerBuilder.RegisterModule<DefaultModule>(); containerBuilder.Populate(services); var container = containerBuilder.Build(); return new AutofacServiceProvider(container); }
public class DefaultModule : Module { protected override void Load(ContainerBuilder builder) { builder.RegisterType<CharacterRepository>().As<ICharacterRepository>(); } }