-
Notifications
You must be signed in to change notification settings - Fork 33
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[ODS-5622] Make the ODSStartup configurable #1207
Conversation
|
||
if (startupType == null) | ||
{ | ||
throw new MissingMemberException(string.Format(MissingStartupClassMessage, startupClassName)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use TypeLoadException
(see documentation here) rather than MissingMemberException
which does not match semantically with what is happening here.
return startupType; | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a blank lines at the end of the files.
{ | ||
public static class StartupFactory | ||
{ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove the blank line here.
private const string DefaultStartupClassName = "Startup"; | ||
public static OdsStartupBase GetStartup(WebHostBuilderContext webHostBuilderContext) | ||
{ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove the blank line here.
.AddJsonFile($"appsettings.{webHostBuilderContext.HostingEnvironment.EnvironmentName}.json", optional: true) | ||
.Build(); | ||
|
||
string startupClassName = config.GetValue<string>("StartUpClassName")?? DefaultStartupClassName; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Treat "Startup" as one word (as .NET does), not two. This means that this setting should be named StartupClassName
not StartUpClassName
.
@@ -37,7 +38,7 @@ public static async Task Main(string[] args) | |||
webBuilder.ConfigureKestrel( | |||
serverOptions => { serverOptions.AddServerHeader = false; }); | |||
|
|||
webBuilder.UseStartup<Startup>(); | |||
webBuilder.UseStartup<OdsStartupBase>(context => StartupFactory.GetStartup(context)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Look at Line 26 above. You could add a line after that like the one below to read the StartupClassName setting into a variable which is then used to drive the logic in this section:
string startupClassName = config.GetValue<string>("StartupClassName");
With a value of "Startup" or a call to string.IsNullOrEmpty
returning true
, you would then just use the original code that was here:
webBuilder.UseStartup<Startup>();
With any other value, you'd then proceed down the path of logic you have, but I'd suggest skipping the rebuild of the configuration there (currently at line 24 of StartupFactory.cs), preferring instead an approach making the StartupFactory
class non-static, and passing the startup class name value to its constructor.
Then, the webBuilder.UseStartup
method overload that accepts a factory method is passed the public instance method rather than a static method, and all the logic related to building the configuration and extracting the "StartupClassName" setting is removed from the factory.
Here's my suggestion for the first part of the Main
method:
string startupClassName;
using (var hostBuilderInitial = Host.CreateDefaultBuilder(args).Build())
{
var config = hostBuilderInitial.Services.GetService<IConfiguration>();
startupClassName = config.GetValue<string>("StartupClassName");
AssemblyLoaderHelper.LoadAssembliesFromFolder(
AssemblyLoaderHelper.GetPluginFolder(
config?.GetValue<string>("Plugin:Folder") ?? string.Empty));
}
var hostBuilder = Host.CreateDefaultBuilder(args)
.ConfigureLogging(ConfigureLogging)
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureWebHostDefaults(
webBuilder =>
{
webBuilder.ConfigureKestrel(
serverOptions => { serverOptions.AddServerHeader = false; });
if (string.IsNullOrEmpty(startupClassName) || startupClassName.EqualsIgnoreCase("Startup"))
{
webBuilder.UseStartup<Startup>();
}
else
{
var startupFactory = new StartupFactory(startupClassName);
webBuilder.UseStartup(context => startupFactory.Create(context));
}
});
And the instance-based startup factory:
public class StartupFactory
{
private readonly string _startupClassName;
public StartupFactory(string startupClassName)
{
_startupClassName = startupClassName;
}
public OdsStartupBase Create(WebHostBuilderContext arg)
{
// Scanning logic here based on _startupClassName
}
}
{ | ||
|
||
private const string MissingStartupClassMessage = | ||
"Could not find the startup class. No loaded class member could be found matching the configured startup class name '{0}'."; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change this message to:
Could not find a startup class named '{0}' that derives from OdsStartupBase.
@@ -12,6 +12,7 @@ | |||
}, | |||
"Tenants": { | |||
}, | |||
"StartUpClassName": "Startup", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rename this setting to "StartupClassName", and default it to an empty string (which should cause the existing Startup
class to be used by default, as described above). This also reduces the chance of this change breaking someone who inadvertently misses the setting in their configuration.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks pretty good now ... just one more tweak.
public OdsStartupBase Create(WebHostBuilderContext webHostBuilderContext) | ||
{ | ||
var startupType = FindStartupClassType(_startupClassName); | ||
var startup = (OdsStartupBase)Activator.CreateInstance(startupType, webHostBuilderContext.Configuration); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In testing, this is missing an argument from the custom startup class' constructor, which we can easily pass along.
Updated code should appear as follows:
var startup = (OdsStartupBase)Activator.CreateInstance(
startupType,
webHostBuilderContext.HostingEnvironment,
webHostBuilderContext.Configuration);
.Where(assembly => assembly.GetName().Name?.StartsWith("EdFi.") ?? false) | ||
.SelectMany(assembly => assembly.GetTypes()) | ||
.Where(t => t.Name.EqualsIgnoreCase(startupClassName) && typeof(OdsStartupBase).IsAssignableFrom(t)) | ||
.FirstOrDefault(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While we're in this file, simplify the statement by replacing the use of Where
with FirstOrDefault
(rather than repeating it at the end) since FirstOrDefault
also accepts a predicate (so the Where
can be eliminated).
} | ||
return startupType; | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove this blank line.
@@ -12,6 +12,7 @@ | |||
}, | |||
"Tenants": { | |||
}, | |||
"StartUpClassName": "", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change the capitalization of the name in this setting to StartupClassName
(Startup is treated as one word, not two).
Update EdFi.Ods.WebApi project Appsettings as:
EDIT: @gmcelhanon changed the sample code to include "debug" trace statements for visibility in the debug output.
Add the
CustomStartup
class below for testing this change: