using System; using System.IO; using System.Threading.Tasks; using Autofac; using Autofac.Extensions.DependencyInjection; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Serilog; namespace MfGames.ToolBuilder { /// /// A builder pattern for creating the tool. This wraps much of the hosting /// infrastructure with some opinionated decisions and reduces the amount of /// boilerplate needed to configure the tool. /// public class ToolBuilder { private readonly string[] arguments; private readonly ConfigToolService configService; private readonly IHostBuilder hostBuilder; private readonly LoggingToolService loggingService; public ToolBuilder( string applicationName, string internalName, string[] arguments) { // Create our various services. this.arguments = arguments; this.ApplicationName = applicationName; this.InternalName = internalName; this.configService = new ConfigToolService() .WithInternalName(this.InternalName); this.loggingService = new LoggingToolService(); // Set up logging first so we can report the loading process. This // sets up the Serilog.Log.Logger which means we can use that for // everything beyond this point. this.loggingService.Configure(arguments); // Start up the basic configuration. this.hostBuilder = Host .CreateDefaultBuilder(arguments) .UseConsoleLifetime() .ConfigureAppConfiguration(this.ConfigureAppConfiguration) .UseSerilog() .UseServiceProviderFactory(new AutofacServiceProviderFactory()) .ConfigureServices(this.ConfigureServices) .ConfigureContainer(this.ConfigureContainer); } /// /// Gets the human-readable name of the application. /// public string ApplicationName { get; } /// /// Gets the internal name of the application. /// public string InternalName { get; } public static ToolBuilder Create( string applicationName, string internalName, string[] arguments) { return new ToolBuilder(applicationName, internalName, arguments); } public ToolBuilder ConfigureContainer( Action configure) { this.hostBuilder.ConfigureContainer(configure); return this; } public ToolBuilder ConfigureServices( Action configure) { this.hostBuilder.ConfigureServices(configure); return this; } /// /// Finishes building the tool, parses the arguments, and runs the /// command. /// /// An error code, 0 for successful, otherwise false. public async Task RunAsync() { try { await this.hostBuilder .RunConsoleAsync() .ConfigureAwait(false); } catch (Exception exception) { Log.Fatal( exception, "There was a problem running the command: {Arguments}", this.arguments); return Environment.ExitCode == 0 ? 1 : Environment.ExitCode; } // Get the exit code and return it. return Environment.ExitCode; } private void ConfigureAppConfiguration( HostBuilderContext context, IConfigurationBuilder builder) { builder.SetBasePath(Directory.GetCurrentDirectory()); this.configService.Configure(builder, this.arguments); } private void ConfigureContainer( HostBuilderContext context, ContainerBuilder builder) { // We want to get logging up and running as soon as possible. We // also hook up the logging to the process exit in an attempt to // make sure the logger is properly flushed before exiting. builder.RegisterInstance(Log.Logger).As().SingleInstance(); AppDomain.CurrentDomain.ProcessExit += (_, _) => Log.CloseAndFlush(); // Register the components required to make the CLI work. builder.RegisterModule(); } private void ConfigureServices( HostBuilderContext context, IServiceCollection services) { services.AddAutofac(); services.AddHostedService(); } } }