04. parse the StartUp source code of Asp. Net Core 2.0, asp. netstartup
04. parse Asp. in the previous article, we set up our own Web application step by step. We created a new StartUp class with only one Configure method and did not inherit from any interfaces, that is to say, Asp. the Net Core 2.0 Framework does not use interfaces to define how to customize the StartUp class for developers. How is this class used by the framework? Download the open source code of Asp. Net Core 2.0 first! I. Review the code for accessing the StartUp class of the framework.
Using Microsoft. aspNetCore. hosting; namespace MyWeb {class Program {static void Main (string [] args) {var host = new WebHostBuilder (). useKestrel () // specify the WebServer as Kestrel. useStartup <StartUpB> () // configure WebHost. build (); host. run (); // start WebHost }}}
The key code for framework access is the WebHostBuilder. UseStartup method. Let's take a look at the framework source code:
public static IWebHostBuilder UseStartup(this IWebHostBuilder hostBuilder, Type startupType) { var startupAssemblyName = startupType.GetTypeInfo().Assembly.GetName().Name; return hostBuilder .UseSetting(WebHostDefaults.ApplicationKey, startupAssemblyName) .ConfigureServices(services => { if (typeof(IStartup).GetTypeInfo().IsAssignableFrom(startupType.GetTypeInfo())) { services.AddSingleton(typeof(IStartup), startupType); } else { services.AddSingleton(typeof(IStartup), sp => { var hostingEnvironment = sp.GetRequiredService<IHostingEnvironment>(); return new ConventionBasedStartup(StartupLoader.LoadMethods(sp, startupType, hostingEnvironment.EnvironmentName)); }); } }); }
First, this is the extension class of the IWebHostBuilder interface. Here there are two branches
1. If StartUp starts fromIStartupAnd add them to the plug-in service framework as a singleton.
2. If it is not fromIStartupInherit, thenPackagingIsIStartupAnd then add it to the plug-in service framework as a singleton.
The Source Code confirms that the ConventionBasedStartup class inheritsIStartup.
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; } } }
View Code
Ii. How does the Framework Package our StartUp class?
From the source code, we can see that the key code isStartupLoader. LoadMethodsLet's look at the framework source code (some code is omitted)
public static StartupMethods LoadMethods(IServiceProvider hostingServiceProvider, Type startupType, string environmentName) { var configureMethod = FindConfigureDelegate(startupType, environmentName); var servicesMethod = FindConfigureServicesDelegate(startupType, environmentName); object instance = null; if (!configureMethod.MethodInfo.IsStatic || (servicesMethod != null && !servicesMethod.MethodInfo.IsStatic)) { instance = ActivatorUtilities.GetServiceOrCreateInstance(hostingServiceProvider, startupType); } Func<IServiceCollection, IServiceProvider> configureServices = services => { return services.BuildServiceProvider(); }; return new StartupMethods(instance, configureMethod.Build(instance), configureServices); }
We guessFindConfigureDelegateMethod connected to our StartUp, the source code confirmed that the Framework obtained ourStartUp. ConfigureMethod: It was originally matched using the method name string class ^_^
private static ConfigureBuilder FindConfigureDelegate(Type startupType, string environmentName) { var configureMethod = FindMethod(startupType, "Configure{0}", environmentName, typeof(void), required: true); return new ConfigureBuilder(configureMethod); }
3. Let our StartUp inherit fromIStartup
From the above analysis, we can see that the framework can be connected to two types of StartUp,
- One is inherited fromIStartupClass
- The other is includeConfigureMethod class
In this case, ourStartUpCan I directly inherit fromIStartupWhat about it? The experiment proves that the Code is as follows:
Using System; using Microsoft. aspNetCore. hosting; using Microsoft. aspNetCore. builder; using Microsoft. aspNetCore. http; using Microsoft. extensions. dependencyInjection; namespace MyWeb {class StartUpI: IStartup {public void Configure (IApplicationBuilder app) {app. run (c => {var req = c. request. path. toString (). trimStart ('/'); var res = string. empty; switch (req) {case "1": res = "one"; break; case "2": res = "two"; break; default: res = "none"; break;} var mtd = string. empty; switch (c. request. method) {case "GET": mtd = "Request Method: get"; break; case "POST": mtd = "Request Method: post"; break; default: mtd = "Request Method: none"; break;} return c. response. writeAsync (res) ;});} public IServiceProvider ConfigureServices (IServiceCollection services) {return services. buildServiceProvider ();}}}
View Code
We pass this class to the framework.
Using Microsoft. aspNetCore. hosting; namespace MyWeb {class Program {static void Main (string [] args) {var host = new WebHostBuilder (). useKestrel () // specify the WebServer as Kestrel. useStartup <StartUpI> () // configure WebHost. build (); host. run (); // start WebHost }}}
Then run the program and the result is as expected: OK!
4. The framework implements a base class integrated from IStartup
By searching the reference relationship of IStartup, we can find that the framework implementsAbstractionBase ClassStartupBase:
public abstract class StartupBase : IStartup { public abstract void Configure(IApplicationBuilder app); IServiceProvider IStartup.ConfigureServices(IServiceCollection services) { ConfigureServices(services); return CreateServiceProvider(services); } public virtual void ConfigureServices(IServiceCollection services) { } public virtual IServiceProvider CreateServiceProvider(IServiceCollection services) { return services.BuildServiceProvider(); } }
As we can see, you only need to implementConfigureThisAbstract MethodThe StartUp customization can be completed to reduce the development workload. we can implement a subclass:
Using Microsoft. aspNetCore. hosting; using Microsoft. aspNetCore. builder; using Microsoft. aspNetCore. http; namespace MyWeb {class StartUpB: StartupBase {public override void Configure (IApplicationBuilder app) {app. run (c => {var req = c. request. path. toString (). trimStart ('/'); var res = string. empty; switch (req) {case "1": res = "one"; break; case "2": res = "two"; break; default: res = "none"; break;} var mtd = string. empty; switch (c. request. method) {case "GET": mtd = "Request Method: get"; break; case "POST": mtd = "Request Method: post"; break; default: mtd = "Request Method: none"; break;} return c. response. writeAsync (res );});}}}
View Code
We pass this class to the framework.
Using Microsoft. aspNetCore. hosting; namespace MyWeb {class Program {static void Main (string [] args) {var host = new WebHostBuilder (). useKestrel () // specify the WebServer as Kestrel. useStartup <StartUpB> () // configure WebHost. build (); host. run (); // start WebHost }}}
Then run the program and the result is as expected: OK!
V. Performance Analysis
Now we have figured out how ASP. Net Core2.0 can access the StartUp class in three ways:
1. Define a class by yourself, which must containConfigureMethod
2. inherited fromIStartup, Implement all methods
3. inherited fromStartupBaseAbstract class, which only needs to be implementedConfigureMethod
I think the third method is more efficient, for two reasons:
1. You only need to implement one method with the least code
2. Reflection is not required and the efficiency is higher.
I don't know. The first method is used by default when Microsoft creates an ASP. Net Core2.0 project. From which perspective ???