我正在制作一个 .NET Core 2.0 应用程序,我需要配置它。我正在查看这个文档,似乎在 .NET Core 1.0 中你可以这样做:
var appConfig = new AppSettings();
config.GetSection("App").Bind(appConfig);
在 .NET Core 1.1 中你可以这样做:
var appConfig = config.GetSection("App").Get<AppSettings>();
但是 .NET Core 2.0 中不存在
Bind()
和 Get<T>()
。实现这一目标的新方法是什么?
您仍然可以执行这两项操作。由于您位于控制台应用程序中,因此可能不使用 ASP.NET Core 元包,因此您需要确保具有正确的依赖项。
为了将配置绑定到对象,您需要
Microsoft.Extensions.Configuration.Binder
包。那么,这两种解决方案都应该可以正常工作。
顺便说一句。即使您在控制台应用程序中,您仍然可以使用 ASP.NET Core 附带的依赖项注入容器。我个人发现它的设置非常简单,因此如果您仍然可以修改应用程序来使用它,那么它可能是值得的。设置看起来像这样:
var configuration = new ConfigurationBuilder()
.AddJsonFile("config.json", optional: false)
.Build();
var services = new ServiceCollection();
services.AddOptions();
// add your services here
services.AddTransient<MyService>();
services.AddTransient<Program>();
// configure options
services.Configure<AppSettings>(configuration.GetSection("App"));
// build service provider
var serviceProvider = services.BuildServiceProvider();
// retrieve main application instance and run the program
var program = serviceProvider.GetService<Program>();
program.Run();
然后,所有注册的服务都可以采用依赖项,就像在 ASP.NET Core 中一样。为了使用您的配置,您可以像平常一样注入
IOptions<AppSettings>
类型。
我仍然对此有疑问,直到今天我终于弄清楚了。
代码运行没有问题,但所有属性仍然为空,即使在绑定之后也是如此。我正在这样做:
public class AppSettings
{
public string MyProperty
}
事实证明你必须这样做:
public class AppSettings
{
public string MyProperty { get; set; }
}
它仅在您的类具有属性而不是字段时才有效。这对我来说并不清楚。
如果您想在
Startup
期间注册配置,请将其添加到Startup.cs
:
services.Configure<AppSettings>(Configuration.GetSection("App"));
然后您可以通过注入
IOptions<>
的实例来访问:
private readonly AppSettings _appSettings;
public MyClass(IOptions<AppSettings> appSettings) {
_appSettings = appSettings.Value;
}
这就是我绑定设置对象并将它们添加为 .Net Core 3.0 中的单例的方式
public void ConfigureServices(IServiceCollection services)
{
var jwtSettings = new JwtSettings();
Configuration.Bind(jwtSettings);
services.AddSingleton(jwtSettings);
var databaseSettings = new DatabaseSettings();
Configuration.Bind(databaseSettings);
services.AddSingleton(databaseSettings);
services.AddControllersWithViews();
}
我的设置对象如下所示:
public class DatabaseSettings
{
public string ConnectionString { get; set; }
public string DatabaseName { get; set; }
}
public class JwtSettings
{
public string Secret { get; set; }
public string Lifetime { get; set; }
}
我的 appsettings.json 文件如下所示:
{
"DatabaseSettings": {
"ConnectionString": "mongodb://localhost:27017",
"DatabaseName": "TestDb"
},
"JwtSettings": {
"Secret": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"Lifetime": "170"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}
为了更简单的配置,我创建了一个帮助器类,它扫描配置对象中的嵌套配置,然后尝试在加载的程序集中找到相应的类并使用给定的配置对其进行初始化。
appsettings.json:
{
"MyState": {
"SomeSimpleValue": "Hello World",
"MyTimeSpan": "00:15:00"
}
}
MyStateOptions.cs
// Class has same name as in appsettings.json with Options suffix.
public class MyStateOptions
{
// Properties must be deserializable from string
// or a class with a default constructor that has
// only properties that are deserializable from string.
public string SomeSimpleValue { get; set; }
public DateTime MyTimeSpan { get; set; }
}
Startup.cs
public class Startup
{
public IConfigurationRoot Configuration { get; }
public Startup(IHostingEnvironment env)
{
// Create configuration as you need it...
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile(...)
.AddEnvironmentVariables();
// Save configuration in property to access it later.
Configuration = builder.Build();
}
public void ConfigureServices(IServiceCollection services)
{
// Register all your desired services...
services.AddMvc(options => ...);
// Call our helper method
services.RegisterOptions(Configuration);
}
}
HelperClass.cs
public static class IServiceCollectionExtensions
{
public static void RegisterOptions(
this IServiceCollection services,
IConfiguration configuration)
{
// Create all options from the given configuration.
var options = OptionsHelper.CreateOptions(configuration);
foreach (var option in options)
{
// We get back Options<MyOptionsType> : IOptions<MyOptionsType>
var interfaces = option.GetType().GetInterfaces();
foreach (var type in interfaces)
{
// Register options IServiceCollection
services.AddSingleton(type, option);
}
}
}
}
OptionsHelper.cs
public static class OptionsHelper
{
public static IEnumerable<object> CreateOptions(IConfiguration configuration)
{
// Get all sections that are objects:
var sections = configuration.GetChildren()
.Where(section => section.GetChildren().Any());
foreach (var section in sections)
{
// Add "Options" suffix if not done.
var name = section.Key.EndsWith("Options")
? section.Key
: section.Key + "Options";
// Scan AppDomain for a matching type.
var type = FirstOrDefaultMatchingType(name);
if (type != null)
{
// Use ConfigurationBinder to create an instance with the given data.
var settings = section.Get(type);
// Encapsulate instance in "Options<T>"
var options = CreateOptionsFor(settings);
}
}
}
private static Type FirstOrDefaultMatchingType(string typeName)
{
// Find matching type that has a default constructor
return AppDomain.CurrentDomain.GetAssemblies()
.Where(assembly => !assembly.IsDynamic)
.SelectMany(assembly => assembly.GetTypes())
.Where(type => type.Name == typeName)
.Where(type => !type.IsAbstract)
.Where(type => type.GetMatchingConstructor(Type.EmptyTypes) != null)
.FirstOrDefault();
}
private static object CreateOptionsFor(object settings)
{
// Call generic method Options.Create<TOptions>(TOptions options)
var openGeneric = typeof(Options).GetMethod(nameof(Options.Create));
var method = openGeneric.MakeGenericMethod(settings.GetType());
return method.Invoke(null, new[] { settings });
}
}
完成所有这些操作后,您可以在服务集合中拥有一个服务,该服务在其构造函数中需要一个
IOptions<MyStateOptions>
,并且无需显式配置您拥有的每个选项即可获得它。只需使用所需的服务和选项实例创建一个新项目即可。将项目添加到您的主项目并将所需的配置添加到您的 appsettings.json。
示例服务.cs
public class MyExampleService
{
private readonly MyStateOptions _options;
public MyExampleService(IOptions<MyStateOptions> options)
{
_options = options?.Value ?? throw new ArgumentNullException(nameof(options));
}
}