Как извлечь список из appsettings.json в ядре .NET

У меня есть файл appsettings.json, который выглядит так:

{
    "someSetting": {
        "subSettings": [
            "one",
            "two",
            "three"
         ]
    }
}

Когда я создаю свой корень конфигурации и делаю что-то вроде config["someSetting:subSettings"], он возвращает null, а фактические доступные настройки выглядят примерно так:

config["someSettings:subSettings:0"]

Есть ли лучший способ получить содержимое someSettings:subSettings в виде списка?


person devlife    schedule 26.08.2016    source источник
comment
Может быть, это работает .. weblog.west-wind.com/posts/2016/may/23/   -  person Venky    schedule 26.08.2016
comment
Может быть. Я использую консольное приложение, которое не является asp.net, но я посмотрю, смогу ли я получить коллекцию сервисов.   -  person devlife    schedule 26.08.2016
comment
Да, это работает и в консольных приложениях. Ничего особенного в asp.net   -  person Victor Hurdugaci    schedule 26.08.2016
comment
Я спрашиваю только потому, что получаю следующее: Could not load file or assembly 'Microsoft.Extensions.Configuration.Binder, Version=1.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'. The system cannot find the file specified   -  person devlife    schedule 29.08.2016
comment
Вы также можете использовать класс DTO для анализа конфигурации   -  person VMAtm    schedule 03.10.2017


Ответы (6)


Вы можете использовать связыватель конфигурации, чтобы получить строгое представление источников конфигурации.

Это пример из теста, который я написал ранее, надеюсь, он поможет:

    [Fact]
    public void BindList()
    {
        var input = new Dictionary<string, string>
        {
            {"StringList:0", "val0"},
            {"StringList:1", "val1"},
            {"StringList:2", "val2"},
            {"StringList:x", "valx"}
        };

        var configurationBuilder = new ConfigurationBuilder();
        configurationBuilder.AddInMemoryCollection(input);
        var config = configurationBuilder.Build();

        var list = new List<string>();
        config.GetSection("StringList").Bind(list);

        Assert.Equal(4, list.Count);

        Assert.Equal("val0", list[0]);
        Assert.Equal("val1", list[1]);
        Assert.Equal("val2", list[2]);
        Assert.Equal("valx", list[3]);
    }

Важная часть - это звонок Bind.

Тест и другие примеры находятся на

person Victor Hurdugaci    schedule 26.08.2016
comment
Примечание для себя: зависит от Microsoft.Extensions.Configuration.Binder - person Stefan Steiger; 22.02.2017
comment
Есть какие-нибудь советы, как это сделать с services.Configure<TOptions>? Я хочу ввести параметры из полей конфигурации, которые включают массив - person Adam; 08.10.2017

Предположим, ваш appsettings.json выглядит так:

{
  "foo": {
    "bar": [
      "1",
      "2",
      "3"
    ]
  }
}

Вы можете извлечь элементы списка следующим образом:

Configuration.GetSection("foo:bar").Get<List<string>>()
person Kirill Rakhman    schedule 17.02.2017
comment
Это сработало для меня, но мне сначала пришлось установить пакет NuGet Microsoft.Extensions.Configuration.Binder, как описано здесь. - person Glorfindel; 05.07.2018
comment
а чтобы получить пару объектов «ключ-значение», вы должны создать классы C # с помощью json2csharp, а затем использовать Configuration.GetSection (foo) .Get ‹List ‹Bar›› () - person Markus; 27.03.2019
comment
Это должно быть ответом. Спасибо @Glorfindel за дополнительный совет! - person Casey Crookston; 22.05.2021

В .NetCore я сделал вот что:

Нормальная настройка:

В вашем appsettings.json создайте раздел конфигурации для ваших пользовательских определений:

    "IDP": [
    {
      "Server": "asdfsd",
      "Authority": "asdfasd",
      "Audience": "asdfadf"
    },
    {
      "Server": "aaaaaa",
      "Authority": "aaaaaa",
      "Audience": "aaaa"
    }
  ]

Создайте класс для моделирования объектов:

public class IDP
{
    public String Server { get; set; }
    public String Authority { get; set; }
    public String Audience { get; set; }

}

в вашем автозагрузке - ›ConfigureServices

services.Configure<List<IDP>>(Configuration.GetSection("IDP"));

Примечание: если вам нужно немедленно получить доступ к вашему списку в методе ConfigureServices, вы можете использовать ...

var subSettings = Configuration.GetSection("IDP").Get<List<IDP>>();

Тогда в вашем контроллере что-то вроде этого:

Public class AccountController: Controller
{
    private readonly IOptions<List<IDP>> _IDPs;
    public AccountController(IOptions<List<Defined>> IDPs)
    {
        _IDPs = IDPs;
    }
  ...
}

просто в качестве примера я использовал его в другом месте в указанном выше контроллере следующим образом:

       _IDPs.Value.ForEach(x => {
            // do something with x
        });

Edge Case

В случае, если вам нужно несколько конфигураций, но они не могут быть в массиве, и вы не знаете, сколько подпараметров у вас будет одновременно. Используйте следующий метод.

appsettings.json

"IDP": {
    "0": {
      "Description": "idp01_test",
      "IDPServer": "https://intapi.somedomain.com/testing/idp01/v1.0",
      "IDPClient": "someapi",
      "Format": "IDP"
    },
    "1": {
      "Description": "idpb2c_test",
      "IDPServer": "https://intapi.somedomain.com/testing/idpb2c",
      "IDPClient": "api1",
      "Format": "IDP"
    },
    "2": {
      "Description": "MyApp",
      "Instance": "https://sts.windows.net/",
      "ClientId": "https://somedomain.com/12345678-5191-1111-bcdf-782d958de2b3",
      "Domain": "somedomain.com",
      "TenantId": "87654321-a10f-499f-9b5f-6de6ef439787",
      "Format": "AzureAD"
    }
  }

Модель

public class IDP
{
    public String Description { get; set; }
    public String IDPServer { get; set; }
    public String IDPClient { get; set; }
    public String Format { get; set; }
    public String Instance { get; set; }
    public String ClientId { get; set; }
    public String Domain { get; set; }
    public String TenantId { get; set; }
}

Создать расширение для объекта Expando

public static class ExpandObjectExtension
    {
        public static TObject ToObject<TObject>(this IDictionary<string, object> someSource, BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public)
               where TObject : class, new()
        {
            Contract.Requires(someSource != null);
            TObject targetObject = new TObject();
            Type targetObjectType = typeof(TObject);

            // Go through all bound target object type properties...
            foreach (PropertyInfo property in
                        targetObjectType.GetProperties(bindingFlags))
            {
                // ...and check that both the target type property name and its type matches
                // its counterpart in the ExpandoObject
                if (someSource.ContainsKey(property.Name)
                    && property.PropertyType == someSource[property.Name].GetType())
                {
                    property.SetValue(targetObject, someSource[property.Name]);
                }
            }

            return targetObject;
        }
    }

ConfigureServices

var subSettings = Configuration.GetSection("IDP").Get<List<ExpandoObject>>();

var idx = 0;
foreach (var pair in subSettings)
{

    IDP scheme = ((ExpandoObject)pair).ToObject<IDP>();
    if (scheme.Format == "AzureAD")
    {
        // this is why I couldn't use an array, AddProtecedWebApi requires a path to a config section
        var section = $"IDP:{idx.ToString()}";
        services.AddProtectedWebApi(Configuration, section, scheme.Description);
        // ... do more stuff
        
    }
    idx++;
}
person Post Impatica    schedule 20.05.2019
comment
Я создал класс для привязки к public class Definitions : List<Defined> {}. `{Определения: [{Имя: somename, Title: sometitle, Изображение: некоторый URL-адрес изображения}, {Имя: somename, Title: sometitle, Изображение: некоторый URL-адрес изображения}]}` - person devmb; 23.09.2019

var settingsSection = config.GetSection["someSettings:subSettings"];
var subSettings = new List<string>;

foreach (var section in settingsSection.GetChildren())
{
    subSettings.Add(section.Value);
}

Это должно дать вам необходимые значения, хранящиеся в subSettings

Приносим извинения за то, что подняли полустарую ветку. Мне было трудно найти ответ, так как многие методы устарели, например Get и GetValue. Это должно быть хорошо, если вам нужно только простое решение без привязки конфигурации. :)

person Marc S    schedule 24.07.2019
comment
Простой ответ, но для меня с некоторыми изменениями: var settingsSection = config.GetSection("someSettings:subSettings");. Другими словами, [] заменяется на (). - person ehsan_kabiri_33; 20.01.2021

В моем случае конфигурация

 services.Configure<List<ApiKey>>(Configuration.GetSection("ApiKeysList"));

не был загружен, потому что свойства были доступны только для чтения и не было конструктора по умолчанию

// Не работает

  public class ApiKey : IApiKey
    {
        public ApiKey(string key, string owner)
        {
            Key = key;
            OwnerName = owner;
        }
        public string Key { get;  }
        public string OwnerName { get;}
    } 

// Работаем

    public class ApiKey : IApiKey
    {
        public ApiKey(){}//Added default constructor
        public ApiKey(string key, string owner)
        {
            Key = key;
            OwnerName = owner;
        }
        public string Key { get; set; }        //Added set property
        public string OwnerName { get; set; }  //Added set property
    } 
person Michael Freidgeim    schedule 17.08.2020

Просто получение всего раздела заполнит свойство List; в классе настроек.

services.Configure<Settings>(configuration.GetSection("Another:Some:Example"));

Но ... помните, что если в классе настроек для этого списка установлены значения по умолчанию ... что настройки конфигурации будут добавочными и, следовательно, не будут перезаписывать исходные значения.

Таким образом, эти значения по умолчанию останутся, и вы не сможете удалить их с помощью каких-либо дополнительных настроек.

 public List<string> NonEditableStuff { get; set; } = new() { "XYZ1", "LTOY3" };

Кроме того, если вы также включили поставщик файлов Ini, может быть полезно знать, что для указания списка там ключи не имеют особого значения, если они уникальны, поэтому имеет смысл сохранить ключ и значения там то же самое, чтобы оказаться в списке.

[Another:Some:Example:NonEditableStuff]
value=value
whatever2=whatever2
person edelwater    schedule 13.06.2021