#c# #.net-core #.net-core-configuration
#c# #.net-core #.net-core-configuration
Вопрос:
Предположим, у нас есть следующий конфигурационный файл
{
"TeamRoster:People:0:Name": "Bryan",
"TeamRoster:People:0:City": "Toronto",
"TeamRoster:People:0:Interests": "Code",
"TeamRoster:People:1:Name": "Sebastian",
"TeamRoster:People:1:City": "Vancouver",
"TeamRoster:People:1:ApartmentType": "Condo"
}
И следующие POCOS для представления этой конфигурации:
public class TeamRoster
{
public Person[] People { get; set; }
}
public class Person
{
public string Name { get; set; }
public string City { get; set; }
}
Предполагая, что я использую ConfigurationBuilder для чтения конфигурации ядра dotnet с помощью чего-то вроде:
IConfiguration config =
new ConfigurationBuilder()
.AddJsonFile("file.json")
.Build();
var myConfig = config.GetSection("TeamRoster").Get<TeamRoster>();
Как я могу получить дополнительные свойства, которые не были частью объекта Person (интересы, тип квартиры)?
Мои рассуждения выходят за рамки приведенного выше тривиального примера. В моей модели у меня могут быть десятки дополнительных атрибутов (например, полиморфных типов), и я хотел бы захватить их без необходимости определять их в объекте конфигурации. В идеале я бы хотел сделать это как [JsonExtendedData], но, насколько я понимаю, даже если данные выражаются в формате Json, это всего лишь источник. Он считывается в объект конфигурации, а затем привязывается.
Есть ли способ настроить привязку .net core?
Комментарии:
1. И как вы собираетесь создать класс для дополнительных свойств, если вы не знаете, что будет и сколько.
2. Мне нужен словарь для нераспознанных свойств, точно так же, как JsonExtendedData .
3. Хорошо, и как вы думаете, какой ключ для этого словаря? Просто имя или вся строка?
4. Не уверен, что я понимаю, о чем ты спрашиваешь @Serge. В принципе, любое нераспознанное свойство в базовой конфигурации, которое не сопоставляется со свойствами объекта, должно рассматриваться как «нераспознанное». Таким образом, ключом будет имя свойства.
Ответ №1:
Чтобы пролить некоторый свет на это, IConfigurationRoot
в Microsoft.Расширения.Конфигурация абстрагирует данные конфигурации из базовых источников конфигурации. Данные конфигурации могут поступать из Json, Azure KeyVault, командной строки, переменных среды, Xml, памяти, файлов Ini, пользовательских секретов и отдельных файлов (ключ для каждого файла).
Каждый из этих источников конфигурации загружает свои данные в словарь, где иерархические значения разделяются с помощью ‘:’ . Когда вы извлекаете данные из IConfiguration
объекта, он пытается разрешить значение из каждого источника данных в обратном порядке. Таким образом, последний добавленный источник конфигурации имеет наивысший приоритет.
Сопоставление IConfiguration
объектов с объектами POCO выполняется с помощью ConfigurationBinder . Хотя ConfigurationBinder
поддерживается разрешение значений с использованием TypeConverters, этот сценарий полезен только для сопоставления отдельных строковых значений в словаре конфигурации, а не целых объектов.
ConfigurationBinder
Имеет встроенную поддержку для привязки коллекций, поэтому теоретически следующие два подхода могут быть использованы для следующей конфигурации.
Отделите фиксированные значения от динамических значений
Представьте данные так, чтобы динамические данные были отделены от статических данных.
{
"auth": {
"providers": [
{
"name": "clientsecret",
"loginUrl": "https://login.microsoftonline.com/mytenant/oauth2",
"fields": {
"client_id": "xxx",
"client_secret": "yyy"
"resource": "https://management.azure.com",
"grant_type": "client_credentials"
}
},
...
]
}
}
При привязке динамических полей к a Dictionary<string,string>
связующее сопоставляет поля как пары ключ / значение. Ключевым преимуществом этого подхода является то, что мы можем использовать преобразователи типов и безопасность типов с фиксированными полями.
public class AuthProvider
{
// FIXED FIELDS
public string Name { get; set; }
public Uri LoginUrl { get; set; }
// DYNAMIC FIELDS
public Dictionary<string,string> Fields { get; set; }
}
Сопоставьте все данные как динамические
Немного более хитрый способ — описать все поля в данных как родственные, а затем связать все данные в виде словаря.
{
"auth": {
"providers": [
{
"name": "clientsecret",
"loginurl": "https://somewhere",
"client_id": "xxx",
"client_secret": "yyy",
"scope": "https://url",
"grant_type": "client_credentials"
}
]
}
}
Мы можем просто воспользоваться привязкой непосредственно к словарю. Обратите внимание, что мы используем lose преобразователь типов для поля Uri:
public class AuthProvider : Dictionary<string,string>
{
public string Name => this["name"]
public string LoginUrl => this["loginUrl"]
public Dictionary<string,string> Fields
{
get
{
var propertyNames = GetType().GetProperties().Select(x => x.Name).ToArray();
var keyPairs = this.Where(kvp => !propertyNames.Contains(kvp.Key));
return
new Dictionary<string,string>(
keyPairs,
StringComparer.OrdinalIgnoreCase);
}
}
}