Binding a configuration to an object graph in .NET Core 2.0

时间:2017-12-18 06:20:02

标签: c# .net-core-2.0

I'm making a .NET Core 2.0 app and I need to configure it. I'm looking at this documentation and it seems that in .NET Core 1.0 you could do:

var appConfig = new AppSettings();
config.GetSection("App").Bind(appConfig);

And in .NET Core 1.1 you could do:

var appConfig = config.GetSection("App").Get<AppSettings>();

But neither Bind nor Get exist in .NET Core 2.0. What's the new way to achieve this?

Thanks,

Josh

5 个答案:

答案 0 :(得分:6)

You can still do both of these. Since you are in a console application, and as such likely not using the ASP.NET Core metapackage, you need to make sure to have the correct dependencies.

In order to bind the configuration to an object, you need the public int convert(byte b1, byte b2){ int i1 = (int) (((b2 << 8) + (b1 & 0xFF)) & 0x0000FFFF); short s1 = (short) i1; int i2 = (int) s1; return i2; } package. Then, both solutions should work just fine.


Btw. even if you are in a console application, you could still make use of the dependency injection container that comes with ASP.NET Core. I’ve personally found it very simple to set up, so if you can still modify your application to use it, it might be worth it. The setup would just look like this:

Microsoft.Extensions.Configuration.Binder

Then, all your registered services can take dependencies just like they would do in ASP.NET Core. And to consume your configuration, you could then inject the 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(); type like usually.

答案 1 :(得分:6)

我仍然遇到这个问题,直到今天我终于明白了。

代码运行没有问题,但所有属性仍然为null,即使在绑定之后也是如此。我这样做了:

public class AppSettings
{
    public string MyProperty
}

事实证明你必须这样做:

public class AppSettings
{
    public string MyProperty { get; set; }
}

仅当您的类具有“属性”而非“字段”时才有效。这对我来说并不清楚。

答案 2 :(得分:1)

If you want to register the config during Startup add this to Startup.cs:

services.Configure<AppSettings>(Configuration.GetSection("App"));

which you can then access by injecting an instance of IOptions<>:

private readonly AppSettings _appSettings;
public MyClass(IOptions<AppSettings> appSettings) {
    _appSettings = appSettings.Value;
}

答案 3 :(得分:1)

Just for an easier configuration I created a helper class that scans the configuration object for nested configurations, then tries to find a corresponding class in the loaded assemblies and initialize it with the given configuration.

appsettings.json:

sudo service mesos-master start

sudo service marathon start

MyStateOptions.cs

{
    "MyState": {
        "SomeSimpleValue": "Hello World",
        "MyTimeSpan": "00:15:00"
    }
}

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

HelperClass.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);
    }
}

OptionsHelper.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);
            }
        }
    }
}

After doing all that stuff you can have a service within your service collection that demands in its constructor an 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 }); } } and you'll get it without explicitly configure each and every option you have. Just create a new project with the desired service and options instance. Add the project to your main project and add the desired configuration to your appsettings.json.

ExampleService.cs

IOptions<MyStateOptions>

答案 4 :(得分:0)

这是我绑定设置对象并将其添加为.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": "*"
}