前面分享了.net core Program類的啓動過程已經源代碼介紹,這裏將繼續講Startup類中的兩個約定方法,一個是ConfigureServices,這個方法是用來寫咱們應用程序所依賴的組件。另外一個Configure,它是咱們MVC請求的中間件方法,也就是咱們每一個請求來要執行的過程均可以寫在這個方法裏面。
爲何說Startup類中的兩個方法是基於約定的呢?實際上是這樣的,在.net core Program類Main方法中有個調用了Run方法這個方法從IServiceCollection容器中拿到一個IStartup類型的實例而後調用了IStartup中定義的兩個方法法,若是咱們的Startup類是實現了這個接口的類 那麼就不是基於約定了,直接就可使用,可是咱們發如今vs給咱們生成的Startup類並無實現任何接口,因此就不會是IStartup類型,那麼內部是如何去作的呢? 實際上是這樣的,在註冊Startup實例的時候還有個類型叫作ConventionBasedStartup從名稱上解讀這個類就是轉換爲基礎的Startup,其實倒是也是這樣的,這個類中是實現了IStartup接口,它的兩個方法中分別調用了各自的對用委託,這些委託實際執行的就是咱們Startup類中定義的兩個方法,請看源代碼:html
public class ConventionBasedStartup : IStartup { private readonly StartupMethods _methods; public ConventionBasedStartup(StartupMethods methods) { _methods = methods; } public void Configure(IApplicationBuilder app) { try { _methods.ConfigureDelegate(app); } catch (Exception ex) { if (ex is TargetInvocationException) { ExceptionDispatchInfo.Capture(ex.InnerException).Throw(); } throw; } } public IServiceProvider ConfigureServices(IServiceCollection services) { try { return _methods.ConfigureServicesDelegate(services); } catch (Exception ex) { if (ex is TargetInvocationException) { ExceptionDispatchInfo.Capture(ex.InnerException).Throw(); } throw; } } }
如今Startup類的方法說清楚了,咱們具體來講說方法中的內容,首先說ConfigureServices(IServiceCollection services),這個方法的參數是約定好的,不能隨意改變,裏面的IServiceCollection接口其實就是咱們依賴注入的容器,說的再直白一點就是咱們整個MVC所須要的實例都由IServiceCollection所管理,IServiceCollection有幾個重要的擴展方法,他們都是定義在ServiceCollectionServiceExtensions靜態類中,AddTransient方法,表示用這個方法添加到IServiceCollection容器的實例在須要注入的實例中都是一個全新的實例,AddScoped方法,這個方法表示在一次請求的生命週期內共用一個實例,AddSingleton方法,這個方法表示整個程序共用一個實例,例如日誌服務,IConfiguration服務等都屬於典型Singleton。請看例子:app
public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddTransient<TransientService>(); services.AddTransient<ServiceDemo1>(); services.AddTransient<ServiceDemo2>(); } public void Configure(IApplicationBuilder app, ServiceDemo1 demo1, ServiceDemo2 demo2 ) { demo1.Test(); demo2.Test(); app.Run(async (HttpContext context) => { await context.Response.WriteAsync("test successd"); }); } } public class TransientService { private int _updateCount; public int GetUpdateCount() {
this._updateCount = this._updateCount + 1; return this._updateCount; } } public class ServiceDemo1 { private readonly TransientService _service; public ServiceDemo1(TransientService service) { _service = service; } public void Test() { Console.WriteLine($"我是demo1的計數:{this._service.GetUpdateCount()}"); } } public class ServiceDemo2 { private readonly TransientService _service; public ServiceDemo2(TransientService service) { _service = service; } public void Test() { Console.WriteLine($"我是demo2的計數:{this._service.GetUpdateCount()}"); } }
上面的例子中會產生一下結果,能夠看得出來這兩個注入的TransientService都是全新的實例
async
若是咱們稍微改變一下注入的方法,將本來的 services.AddTransient<TransientService>();改爲services.AddScoped<TransientService>();就會產生以下結果:
ide
這個能說明什麼呢,咱們有兩次注入 這個就表示TransientService保持了以前demo1的狀態 demo1和demo2是能夠共用這個實例來傳輸數據的,AddSingleton方法理解起來比較簡單就不過多絮叨了,上面已經說明。ui
接下來再來講說Startup類中的Configure方法,Configure方法中的參數是能夠變化的,也就是說你能夠用依賴注入的方法在參數中注入你想要注入的實例,前面說了 這個方法是咱們請求的中間件方法,這個方法中會整合咱們注入的IApplicationBuilder 中調用的各類Use方法中定義的中間件 並非說這裏面定義的代碼每次請求都會被執行,這個概念必定要搞清楚,Configure方法只會在啓動的時候執行一次,後面就不會再執行了,Configure方法只是讓咱們能夠定義整個MVC的處理請求的執行順序,具體的能夠看看官方的文檔https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/middleware/?view=aspnetcore-2.2
this
其實中間件都是由IApplicationBuilder 所管理的ApplicationBuilder類實現了IApplicationBuilder 接口中的方法,看到ApplicationBuilder中的源代碼中有個屬性_components 它是一個IList<Func<RequestDelegate, RequestDelegate>>類型的委託容器,容器中的委託就是請求過來須要執行的中間件委託,當你在Configure方法中調用app.UseXXX的時候就會被註冊到這個容器中去,而後請求過來就按照順序執行容器中的每個委託,因此這裏就解釋了前面說的Configure方法只會被執行一次的說法。下面也貼一下ApplicationBuilder類的源代碼:spa
public class ApplicationBuilder : IApplicationBuilder { private readonly IList<Func<RequestDelegate, RequestDelegate>> _components = new List<Func<RequestDelegate, RequestDelegate>>(); public IServiceProvider ApplicationServices { get { return GetProperty<IServiceProvider>(Constants.BuilderProperties.ApplicationServices); } set { SetProperty(Constants.BuilderProperties.ApplicationServices, value); } } public IFeatureCollection ServerFeatures => GetProperty<IFeatureCollection>(Constants.BuilderProperties.ServerFeatures); public IDictionary<string, object> Properties { get; } public ApplicationBuilder(IServiceProvider serviceProvider) { Properties = new Dictionary<string, object>(StringComparer.Ordinal); ApplicationServices = serviceProvider; } public ApplicationBuilder(IServiceProvider serviceProvider, object server) : this(serviceProvider) { SetProperty(Constants.BuilderProperties.ServerFeatures, server); } private ApplicationBuilder(ApplicationBuilder builder) { Properties = new CopyOnWriteDictionary<string, object>(builder.Properties, StringComparer.Ordinal); } private T GetProperty<T>(string key) { if (!Properties.TryGetValue(key, out object value)) { return default(T); } return (T)value; } private void SetProperty<T>(string key, T value) { Properties[key] = value; } public IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware) { _components.Add(middleware); return this; } public IApplicationBuilder New() { return new ApplicationBuilder(this); } public RequestDelegate Build() { RequestDelegate requestDelegate = delegate (HttpContext context) { context.Response.StatusCode = 404; return Task.CompletedTask; }; foreach (Func<RequestDelegate, RequestDelegate> item in _components.Reverse()) { requestDelegate = item(requestDelegate); } return requestDelegate; } }
好啦,這篇關於Startup類就算介紹完成了,下篇開始正式介紹MVC.net