From 55271e8719b3f06381f0f77da9e67ea8d357ff2e Mon Sep 17 00:00:00 2001 From: Louis DeJardin Date: Thu, 8 May 2014 20:38:36 -0700 Subject: [PATCH] Supporting Startup method dependency injection also changes method name from Configuration to Configure also supports enviroment name command line and --- samples/KWebStartup/Startup.cs | 7 +- samples/KWebStartup/project.json | 2 +- .../HostingContext.cs | 1 + src/Microsoft.AspNet.Hosting/HostingEngine.cs | 4 +- src/Microsoft.AspNet.Hosting/Program.cs | 7 +- .../Startup/IStartupLoader.cs | 5 +- .../Startup/IStartupManager.cs | 4 +- .../Startup/NullStartupLoader.cs | 5 +- .../Startup/StartupLoader.cs | 89 ++++++++++++++----- .../Startup/StartupManager.cs | 6 +- 10 files changed, 96 insertions(+), 34 deletions(-) diff --git a/samples/KWebStartup/Startup.cs b/samples/KWebStartup/Startup.cs index a46d8bbc06..dbe847a1be 100644 --- a/samples/KWebStartup/Startup.cs +++ b/samples/KWebStartup/Startup.cs @@ -18,18 +18,19 @@ using Microsoft.AspNet; using Microsoft.AspNet.Builder; using Microsoft.AspNet.Http; +using Microsoft.Framework.Runtime; namespace KWebStartup { public class Startup { - public void Configuration(IBuilder app) + public void Configure(IBuilder app, IApplicationEnvironment env) { app.Run(async context => { context.Response.ContentType = "text/plain"; - await context.Response.WriteAsync("Hello world"); + await context.Response.WriteAsync("Hello world, from " + env.ApplicationName); }); } } -} \ No newline at end of file +} diff --git a/samples/KWebStartup/project.json b/samples/KWebStartup/project.json index 3ed8d30072..e2d56265b7 100644 --- a/samples/KWebStartup/project.json +++ b/samples/KWebStartup/project.json @@ -5,7 +5,7 @@ "Microsoft.AspNet.Http": "0.1-alpha-*", "Microsoft.AspNet.Server.WebListener": "0.1-alpha-*" }, - "commands": { "web": "Microsoft.AspNet.Hosting server.name=Microsoft.AspNet.Server.WebListener server.urls=http://localhost:5001" }, + "commands": { "web": "Microsoft.AspNet.Hosting server=Microsoft.AspNet.Server.WebListener server.urls=http://localhost:5001" }, "configurations": { "net45": { }, diff --git a/src/Microsoft.AspNet.Hosting/HostingContext.cs b/src/Microsoft.AspNet.Hosting/HostingContext.cs index b0d430626d..ea5685e6bb 100644 --- a/src/Microsoft.AspNet.Hosting/HostingContext.cs +++ b/src/Microsoft.AspNet.Hosting/HostingContext.cs @@ -31,6 +31,7 @@ namespace Microsoft.AspNet.Hosting public IBuilder Builder { get; set; } public string ApplicationName { get; set; } + public string EnvironmentName { get; set; } public Action ApplicationStartup { get; set; } public RequestDelegate ApplicationDelegate { get; set; } diff --git a/src/Microsoft.AspNet.Hosting/HostingEngine.cs b/src/Microsoft.AspNet.Hosting/HostingEngine.cs index b1dd6093cb..b8cecf5752 100644 --- a/src/Microsoft.AspNet.Hosting/HostingEngine.cs +++ b/src/Microsoft.AspNet.Hosting/HostingEngine.cs @@ -117,7 +117,9 @@ namespace Microsoft.AspNet.Hosting return; } - context.ApplicationStartup = _startupManager.LoadStartup(context.ApplicationName); + context.ApplicationStartup = _startupManager.LoadStartup( + context.ApplicationName, + context.EnvironmentName); } private class Disposable : IDisposable diff --git a/src/Microsoft.AspNet.Hosting/Program.cs b/src/Microsoft.AspNet.Hosting/Program.cs index 2834105153..00b4a6af5c 100644 --- a/src/Microsoft.AspNet.Hosting/Program.cs +++ b/src/Microsoft.AspNet.Hosting/Program.cs @@ -38,12 +38,12 @@ namespace Microsoft.AspNet.Hosting public void Main(string[] args) { var config = new Configuration(); - config.AddCommandLine(args); if (File.Exists(HostingIniFile)) { config.AddIniFile(HostingIniFile); } config.AddEnvironmentVariables(); + config.AddCommandLine(args); var serviceCollection = new ServiceCollection(); serviceCollection.Add(HostingServices.GetDefaultServices(config)); @@ -55,9 +55,10 @@ namespace Microsoft.AspNet.Hosting { Services = services, Configuration = config, - ServerName = config.Get("server.name"), // TODO: Key names - ApplicationName = config.Get("app.name") // TODO: Key names + ServerName = config.Get("server"), // TODO: Key names + ApplicationName = config.Get("app") // TODO: Key names ?? appEnvironment.ApplicationName, + EnvironmentName = config.Get("env") ?? "Development" }; var engine = services.GetService(); diff --git a/src/Microsoft.AspNet.Hosting/Startup/IStartupLoader.cs b/src/Microsoft.AspNet.Hosting/Startup/IStartupLoader.cs index e8eaede1c7..a6a0741ad7 100644 --- a/src/Microsoft.AspNet.Hosting/Startup/IStartupLoader.cs +++ b/src/Microsoft.AspNet.Hosting/Startup/IStartupLoader.cs @@ -24,6 +24,9 @@ namespace Microsoft.AspNet.Hosting.Startup { public interface IStartupLoader { - Action LoadStartup(string applicationName, IList diagnosticMessages); + Action LoadStartup( + string applicationName, + string environmentName, + IList diagnosticMessages); } } diff --git a/src/Microsoft.AspNet.Hosting/Startup/IStartupManager.cs b/src/Microsoft.AspNet.Hosting/Startup/IStartupManager.cs index 7d4655f36f..1b8b9f19cc 100644 --- a/src/Microsoft.AspNet.Hosting/Startup/IStartupManager.cs +++ b/src/Microsoft.AspNet.Hosting/Startup/IStartupManager.cs @@ -23,6 +23,8 @@ namespace Microsoft.AspNet.Hosting.Startup { public interface IStartupManager { - Action LoadStartup(string applicationName); + Action LoadStartup( + string applicationName, + string environmentName); } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Hosting/Startup/NullStartupLoader.cs b/src/Microsoft.AspNet.Hosting/Startup/NullStartupLoader.cs index c70dad2e65..559d44d5e2 100644 --- a/src/Microsoft.AspNet.Hosting/Startup/NullStartupLoader.cs +++ b/src/Microsoft.AspNet.Hosting/Startup/NullStartupLoader.cs @@ -31,7 +31,10 @@ namespace Microsoft.AspNet.Hosting.Startup public static IStartupLoader Instance { get; private set; } - public Action LoadStartup(string applicationName, IList diagnosticMessages) + public Action LoadStartup( + string applicationName, + string environmentName, + IList diagnosticMessages) { return null; } diff --git a/src/Microsoft.AspNet.Hosting/Startup/StartupLoader.cs b/src/Microsoft.AspNet.Hosting/Startup/StartupLoader.cs index f9629c819f..98aec36c67 100644 --- a/src/Microsoft.AspNet.Hosting/Startup/StartupLoader.cs +++ b/src/Microsoft.AspNet.Hosting/Startup/StartupLoader.cs @@ -30,42 +30,49 @@ namespace Microsoft.AspNet.Hosting.Startup private readonly IServiceProvider _services; private readonly IStartupLoader _next; - public StartupLoader(IServiceProvider services, IStartupLoader next) + public StartupLoader( + IServiceProvider services, + IStartupLoader next) { _services = services; _next = next; } - public Action LoadStartup(string applicationName, IList diagnosticMessages) + public Action LoadStartup( + string applicationName, + string environmentName, + IList diagnosticMessages) { if (String.IsNullOrEmpty(applicationName)) { - return _next.LoadStartup(applicationName, diagnosticMessages); + return _next.LoadStartup(applicationName, environmentName, diagnosticMessages); } - var nameParts = Utilities.SplitTypeName(applicationName); - string typeName = nameParts.Item1; - string assemblyName = nameParts.Item2; - - var assembly = Assembly.Load(new AssemblyName(assemblyName)); + var assembly = Assembly.Load(new AssemblyName(applicationName)); if (assembly == null) { - throw new Exception(String.Format("TODO: assembly {0} failed to load message", assemblyName)); + throw new Exception(String.Format("TODO: assembly {0} failed to load message", applicationName)); } - Type type = null; - if (string.IsNullOrEmpty(typeName)) - { - typeName = "Startup"; - } + var startupName1 = "Startup" + environmentName; + var startupName2 = "Startup"; // Check the most likely places first - type = assembly.GetType(typeName) ?? assembly.GetType(assembly.GetName().Name + "." + typeName); + var type = + assembly.GetType(startupName1) ?? + assembly.GetType(applicationName + "." + startupName1) ?? + assembly.GetType(startupName2) ?? + assembly.GetType(applicationName + "." + startupName2); if (type == null) { // Full scan - var typeInfo = assembly.DefinedTypes.FirstOrDefault(aType => aType.Name.Equals(typeName, StringComparison.OrdinalIgnoreCase)); + var definedTypes = assembly.DefinedTypes.ToList(); + + var startupType1 = definedTypes.Where(info => info.Name.Equals(startupName1, StringComparison.Ordinal)); + var startupType2 = definedTypes.Where(info => info.Name.Equals(startupName2, StringComparison.Ordinal)); + + var typeInfo = startupType1.Concat(startupType2).FirstOrDefault(); if (typeInfo != null) { type = typeInfo.AsType(); @@ -74,18 +81,30 @@ namespace Microsoft.AspNet.Hosting.Startup if (type == null) { - throw new Exception(String.Format("TODO: type {0} failed to load message", typeName)); + throw new Exception(String.Format("TODO: {0} or {1} class not found in assembly {2}", + startupName1, + startupName2, + applicationName)); } - var methodInfo = type.GetTypeInfo().GetDeclaredMethod("Configuration"); + var configureMethod1 = "Configure" + environmentName; + var configureMethod2 = "Configure"; + var methodInfo = type.GetTypeInfo().GetDeclaredMethod(configureMethod1); if (methodInfo == null) { - throw new Exception("TODO: Configuration method not found"); + methodInfo = type.GetTypeInfo().GetDeclaredMethod(configureMethod2); + } + if (methodInfo == null) + { + throw new Exception(string.Format("TODO: {0} or {1} method not found", + configureMethod1, + configureMethod2)); } if (methodInfo.ReturnType != typeof(void)) { - throw new Exception("TODO: Configuration method isn't void-returning."); + throw new Exception(string.Format("TODO: {0} method isn't void-returning.", + methodInfo.Name)); } object instance = null; @@ -93,7 +112,35 @@ namespace Microsoft.AspNet.Hosting.Startup { instance = ActivatorUtilities.GetServiceOrCreateInstance(_services, type); } - return builder => methodInfo.Invoke(instance, new object[] { builder }); + + return builder => + { + var parameterInfos = methodInfo.GetParameters(); + var parameters = new object[parameterInfos.Length]; + for (var index = 0; index != parameterInfos.Length; ++index) + { + var parameterInfo = parameterInfos[index]; + if (parameterInfo.ParameterType == typeof(IBuilder)) + { + parameters[index] = builder; + } + else + { + try + { + parameters[index] = _services.GetService(parameterInfo.ParameterType); + } + catch (Exception ex) + { + throw new Exception(string.Format( + "TODO: Unable to resolve service for startup method {0} {1}", + parameterInfo.Name, + parameterInfo.ParameterType.FullName)); + } + } + } + methodInfo.Invoke(instance, parameters); + }; } } } diff --git a/src/Microsoft.AspNet.Hosting/Startup/StartupManager.cs b/src/Microsoft.AspNet.Hosting/Startup/StartupManager.cs index 16a6a4e2bb..8c4bc7b1ff 100644 --- a/src/Microsoft.AspNet.Hosting/Startup/StartupManager.cs +++ b/src/Microsoft.AspNet.Hosting/Startup/StartupManager.cs @@ -32,7 +32,9 @@ namespace Microsoft.AspNet.Hosting.Startup _providers = providers; } - public Action LoadStartup(string applicationName) + public Action LoadStartup( + string applicationName, + string environmentName) { // build ordered chain of application loaders var chain = _providers @@ -41,7 +43,7 @@ namespace Microsoft.AspNet.Hosting.Startup // invoke chain to acquire application entrypoint and diagnostic messages var diagnosticMessages = new List(); - var application = chain.LoadStartup(applicationName, diagnosticMessages); + var application = chain.LoadStartup(applicationName, environmentName, diagnosticMessages); if (application == null) {