asp.net core 默认配置


Configuration

asp.net core  默认的 appsettings.json 配置是如果注入到承载主机的,

创建 一个asp.net core  MVC 或者api程序,Program.cs 都可以看见承载主机如下:

IHostBuilder CreateHostBuilder(string[] args) =>
      Host.CreateDefaultBuilder(args)
...
Build()
CreateDefaultBuilder的实现:
public static IWebHostBuilder CreateDefaultBuilder(string[] args)
{
var builder = new WebHostBuilder();

if (string.IsNullOrEmpty(builder.GetSetting(WebHostDefaults.ContentRootKey)))
{
builder.UseContentRoot(Directory.GetCurrentDirectory());
}
if (args != null)
{
builder.UseConfiguration(new ConfigurationBuilder().AddCommandLine(args).Build());
}

builder.ConfigureAppConfiguration((hostingContext, config) =>
{
var env = hostingContext.HostingEnvironment;

config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);

if (env.IsDevelopment())
{
var appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName));
if (appAssembly != null)
{
config.AddUserSecrets(appAssembly, optional: true);
}
}

config.AddEnvironmentVariables();

if (args != null)
{
config.AddCommandLine(args);
}
})
.ConfigureLogging((hostingContext, loggingBuilder) =>
{
loggingBuilder.Configure(options =>
{
options.ActivityTrackingOptions = ActivityTrackingOptions.SpanId
| ActivityTrackingOptions.TraceId
| ActivityTrackingOptions.ParentId;
});
loggingBuilder.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
loggingBuilder.AddConsole();
loggingBuilder.AddDebug();
loggingBuilder.AddEventSourceLogger();
}).
UseDefaultServiceProvider((context, options) =>
{
options.ValidateScopes = context.HostingEnvironment.IsDevelopment();
});

ConfigureWebDefaults(builder);

return builder;
}

 HostBuilder:

public class HostBuilder : IHostBuilder
    {
        private List> _configureHostConfigActions = new List>();
        private List> _configureAppConfigActions = new List>();
        private List> _configureServicesActions = new List>();
        private List _configureContainerActions = new List();
        private IServiceFactoryAdapter _serviceProviderFactory = new ServiceFactoryAdapter(new DefaultServiceProviderFactory());
        private bool _hostBuilt;
        private IConfiguration _hostConfiguration;
        private IConfiguration _appConfiguration;
        private HostBuilderContext _hostBuilderContext;
        private HostingEnvironment _hostingEnvironment;
        private IServiceProvider _appServices;

        /// 
        /// A central location for sharing state between components during the host building process.
        /// 
        public IDictionary Properties { get; } = new Dictionary();

        /// 
        /// Set up the configuration for the builder itself. This will be used to initialize the 
        /// for use later in the build process. This can be called multiple times and the results will be additive.
        /// 
        /// The delegate for configuring the  that will be used
        /// to construct the  for the host.
        /// The same instance of the  for chaining.
        public IHostBuilder ConfigureHostConfiguration(Action configureDelegate)
        {
            _configureHostConfigActions.Add(configureDelegate ?? throw new ArgumentNullException(nameof(configureDelegate)));
            return this;
        }

        /// 
        /// Sets up the configuration for the remainder of the build process and application. This can be called multiple times and
        /// the results will be additive. The results will be available at  for
        /// subsequent operations, as well as in .
        /// 
        /// The delegate for configuring the  that will be used
        /// to construct the  for the host.
        /// The same instance of the  for chaining.
        public IHostBuilder ConfigureAppConfiguration(Action configureDelegate)
        {
            _configureAppConfigActions.Add(configureDelegate ?? throw new ArgumentNullException(nameof(configureDelegate)));
            return this;
        }

        /// 
        /// Adds services to the container. This can be called multiple times and the results will be additive.
        /// 
        /// The delegate for configuring the  that will be used
        /// to construct the  for the host.
        /// The same instance of the  for chaining.
        public IHostBuilder ConfigureServices(Action configureDelegate)
        {
            _configureServicesActions.Add(configureDelegate ?? throw new ArgumentNullException(nameof(configureDelegate)));
            return this;
        }

        /// 
        /// Overrides the factory used to create the service provider.
        /// 
        /// The type of the builder to create.
        /// A factory used for creating service providers.
        /// The same instance of the  for chaining.
        public IHostBuilder UseServiceProviderFactory(IServiceProviderFactory factory)
        {
            _serviceProviderFactory = new ServiceFactoryAdapter(factory ?? throw new ArgumentNullException(nameof(factory)));
            return this;
        }

        /// 
        /// Overrides the factory used to create the service provider.
        /// 
        /// A factory used for creating service providers.
        /// The type of the builder to create.
        /// The same instance of the  for chaining.
        public IHostBuilder UseServiceProviderFactory(Func> factory)
        {
            _serviceProviderFactory = new ServiceFactoryAdapter(() => _hostBuilderContext, factory ?? throw new ArgumentNullException(nameof(factory)));
            return this;
        }

        /// 
        /// Enables configuring the instantiated dependency container. This can be called multiple times and
        /// the results will be additive.
        /// 
        /// The type of the builder to create.
        /// The delegate for configuring the  that will be used
        /// to construct the  for the host.
        /// The same instance of the  for chaining.
        public IHostBuilder ConfigureContainer(Action configureDelegate)
        {
            _configureContainerActions.Add(new ConfigureContainerAdapter(configureDelegate
                ?? throw new ArgumentNullException(nameof(configureDelegate))));
            return this;
        }

        /// 
        /// Run the given actions to initialize the host. This can only be called once.
        /// 
        /// An initialized 
        public IHost Build()
        {
            if (_hostBuilt)
            {
                throw new InvalidOperationException("Build can only be called once.");
            }
            _hostBuilt = true;

            BuildHostConfiguration();
            CreateHostingEnvironment();
            CreateHostBuilderContext();
            BuildAppConfiguration();
            CreateServiceProvider();

            return _appServices.GetRequiredService();
        }

        private void BuildHostConfiguration()
        {
            IConfigurationBuilder configBuilder = new ConfigurationBuilder()
                .AddInMemoryCollection(); // Make sure there's some default storage since there are no default providers

            foreach (Action buildAction in _configureHostConfigActions)
            {
                buildAction(configBuilder);
            }
            _hostConfiguration = configBuilder.Build();
        }

        private void CreateHostingEnvironment()
        {
            _hostingEnvironment = new HostingEnvironment()
            {
                ApplicationName = _hostConfiguration[HostDefaults.ApplicationKey],
                EnvironmentName = _hostConfiguration[HostDefaults.EnvironmentKey] ?? Environments.Production,
                ContentRootPath = ResolveContentRootPath(_hostConfiguration[HostDefaults.ContentRootKey], AppContext.BaseDirectory),
            };

            if (string.IsNullOrEmpty(_hostingEnvironment.ApplicationName))
            {
                // Note GetEntryAssembly returns null for the net4x console test runner.
                _hostingEnvironment.ApplicationName = Assembly.GetEntryAssembly()?.GetName().Name;
            }

            _hostingEnvironment.ContentRootFileProvider = new PhysicalFileProvider(_hostingEnvironment.ContentRootPath);
        }

        private string ResolveContentRootPath(string contentRootPath, string basePath)
        {
            if (string.IsNullOrEmpty(contentRootPath))
            {
                return basePath;
            }
            if (Path.IsPathRooted(contentRootPath))
            {
                return contentRootPath;
            }
            return Path.Combine(Path.GetFullPath(basePath), contentRootPath);
        }

        private void CreateHostBuilderContext()
        {
            _hostBuilderContext = new HostBuilderContext(Properties)
            {
                HostingEnvironment = _hostingEnvironment,
                Configuration = _hostConfiguration
            };
        }

        private void BuildAppConfiguration()
        {
            IConfigurationBuilder configBuilder = new ConfigurationBuilder()
                .SetBasePath(_hostingEnvironment.ContentRootPath)
                .AddConfiguration(_hostConfiguration, shouldDisposeConfiguration: true);

            foreach (Action buildAction in _configureAppConfigActions)
            {
                buildAction(_hostBuilderContext, configBuilder);
            }
            _appConfiguration = configBuilder.Build();
            _hostBuilderContext.Configuration = _appConfiguration;
        }

        private void CreateServiceProvider()
        {
            var services = new ServiceCollection();
#pragma warning disable CS0618 // Type or member is obsolete
            services.AddSingleton(_hostingEnvironment);
#pragma warning restore CS0618 // Type or member is obsolete
            services.AddSingleton(_hostingEnvironment);
            services.AddSingleton(_hostBuilderContext);
            // register configuration as factory to make it dispose with the service provider
            services.AddSingleton(_ => _appConfiguration);
#pragma warning disable CS0618 // Type or member is obsolete
            services.AddSingleton(s => (IApplicationLifetime)s.GetService());
#pragma warning restore CS0618 // Type or member is obsolete
            services.AddSingleton();
            services.AddSingleton();
            services.AddSingleton(_ =>
            {
                return new Internal.Host(_appServices,
                    _appServices.GetRequiredService(),
                    _appServices.GetRequiredService>(),
                    _appServices.GetRequiredService(),
                    _appServices.GetRequiredService>());
            });
            services.AddOptions().Configure(options => { options.Initialize(_hostConfiguration); });
            services.AddLogging();

            foreach (Action configureServicesAction in _configureServicesActions)
            {
                configureServicesAction(_hostBuilderContext, services);
            }

            object containerBuilder = _serviceProviderFactory.CreateBuilder(services);

            foreach (IConfigureContainerAdapter containerAction in _configureContainerActions)
            {
                containerAction.ConfigureContainer(_hostBuilderContext, containerBuilder);
            }

            _appServices = _serviceProviderFactory.CreateServiceProvider(containerBuilder);

            if (_appServices == null)
            {
                throw new InvalidOperationException($"The IServiceProviderFactory returned a null IServiceProvider.");
            }

            // resolve configuration explicitly once to mark it as resolved within the
            // service provider, ensuring it will be properly disposed with the provider
            _ = _appServices.GetService();
        }
    }

  Option模式

 Option 的注入

   public static IServiceCollection AddOptions(this IServiceCollection services)
        {
            if (services == null)
            {
                throw new ArgumentNullException(nameof(services));
            }

            services.TryAdd(ServiceDescriptor.Singleton(typeof(IOptions<>), typeof(OptionsManager<>)));
            services.TryAdd(ServiceDescriptor.Scoped(typeof(IOptionsSnapshot<>), typeof(OptionsManager<>)));
            services.TryAdd(ServiceDescriptor.Singleton(typeof(IOptionsMonitor<>), typeof(OptionsMonitor<>)));
            services.TryAdd(ServiceDescriptor.Transient(typeof(IOptionsFactory<>), typeof(OptionsFactory<>)));
            services.TryAdd(ServiceDescriptor.Singleton(typeof(IOptionsMonitorCache<>), typeof(OptionsCache<>)));
            return services;
        }

服务发现

 protected DbConnectionOptions Options { get; }

        public DefaultConnectionStringResolver(IOptionsSnapshot options)
        {
            Options = options.Value;
        }

  示例: IOption<>

appsettings.json

{
  "App": {
    "SelfUrl": "https://localhost:44337",
    "CorsOrigins": "http://localhost:4200,https://localhost:44307,https://localhost:44332"
  },
  "ConnectionStrings": {
    "Default": "Server=localhost;Database='';Trusted_Connection=True;MultipleActiveResultSets=true"
  },
  "Redis": {
    "Configuration": "127.0.0.1"
  }
}

  解析

  public void Test()
        {
            var configuration = new ConfigurationBuilder()
    .SetBasePath(Directory.GetCurrentDirectory())
    .AddJsonFile(path: "appsettings.json", optional: false)
    .Build();

            var options = new ServiceCollection()
                .Configure(configuration)
                .BuildServiceProvider()
                .GetService>()
                .Value;

            Console.WriteLine(options.ConnectionStrings.Default);
            Console.Read();
        }
    }

    public class DbConnectionOptions
    {
        public ConnectionStrings ConnectionStrings { get; set; }

        public DbConnectionOptions()
        {
            ConnectionStrings = new ConnectionStrings();
        }
    }
    [Serializable]
    public class ConnectionStrings : Dictionary
    {
        public const string DefaultConnectionStringName = "Default";

        public string Default
        {
            get => this.GetOrDefault(DefaultConnectionStringName);
            set => this[DefaultConnectionStringName] = value;
        }
    }
    public static class DictionaryExtensions
    {
        public static TValue GetOrDefault(this Dictionary dictionary, TKey key)
        {
            TValue obj;
            return dictionary.TryGetValue(key, out obj) ? obj : default;
        }
    }