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;
}
}