Setiap programmer membayangkan - baik, atau mungkin ingin membayangkan - dirinya sebagai pilot pesawat, ketika Anda memiliki proyek besar, panel besar sensor, metrik, dan sakelar untuknya, yang dengannya Anda dapat dengan mudah mengkonfigurasi semuanya sebagaimana mestinya. Yah, setidaknya jangan berlari untuk mengangkat sasis sendiri secara manual. Baik metrik dan grafik semuanya bagus, tetapi hari ini saya ingin memberi tahu Anda tentang tumbler dan tombol yang sama yang dapat mengubah parameter perilaku pesawat, mengkonfigurasinya.
Pentingnya konfigurasi sulit untuk diremehkan. Setiap orang menggunakan satu atau pendekatan lain dalam mengkonfigurasi aplikasi mereka, dan pada prinsipnya tidak ada yang rumit tentang itu, tetapi apakah sesederhana itu? Saya mengusulkan untuk melihat "sebelum" dan "setelah" dalam konfigurasi dan memahami detailnya: bagaimana cara kerjanya, fitur baru apa yang kita miliki dan bagaimana menggunakannya secara maksimal. Mereka yang tidak terbiasa dengan konfigurasi di .NET Core akan mendapatkan dasar-dasarnya, dan mereka yang sudah familiar akan mendapatkan makanan untuk dipikirkan dan menggunakan pendekatan baru dalam pekerjaan sehari-hari mereka.
Konfigurasi Pre-.NET Core
Pada tahun 2002, .NET Framework diperkenalkan, dan karena saat itu XML hype, pengembang dari Microsoft memutuskan "mari kita miliki di mana-mana", dan sebagai hasilnya, kami mendapatkan konfigurasi XML yang masih hidup. Di bagian atas tabel, kami memiliki kelas ConfigurationManager statis tempat kami mendapatkan representasi string dari nilai parameter. Konfigurasinya sendiri terlihat seperti ini:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="Title" value=".NET Configuration evo" />
<add key="MaxPage" value="10" />
</appSettings>
</configuration>
Masalahnya terpecahkan, para pengembang mendapat opsi penyesuaian, yang lebih baik daripada file INI, tetapi dengan kekhasannya sendiri. Jadi, misalnya, dukungan untuk nilai pengaturan yang berbeda untuk berbagai jenis lingkungan aplikasi diimplementasikan menggunakan transformasi XSLT dari file konfigurasi. Kita dapat mendefinisikan skema XML kita sendiri untuk elemen dan atribut jika kita menginginkan sesuatu yang lebih kompleks dalam hal pengelompokan data. Pasangan nilai-kunci memiliki jenis string yang ketat, dan jika kita memerlukan angka atau tanggal, "mari kita lakukan sendiri":
string title = ConfigurationManager.AppSettings["Title"];
int maxPage = int.Parse(ConfigurationManager.AppSettings["MaxPage"]);
Pada tahun 2005 kami menambahkan bagian konfigurasi , mereka mengizinkan pengelompokan parameter, membangun skema mereka sendiri, menghindari konflik penamaan. Kami juga menyajikan file * .settings dan desainer khusus untuk mereka.
Sekarang Anda bisa mendapatkan kelas yang dihasilkan dan diketik dengan kuat yang mewakili data konfigurasi. Perancang memungkinkan Anda untuk mengedit nilai dengan mudah; pengurutan berdasarkan kolom editor tersedia. Data diambil menggunakan properti Default dari kelas yang dihasilkan, yang menyediakan objek konfigurasi Singleton.
DateTime date = Properties.Settings.Default.CustomDate;
int displayItems = Properties.Settings.Default.MaxDisplayItems;
string name = Properties.Settings.Default.ApplicationName;
Kami juga menambahkan cakupan nilai parameter konfigurasi. Area Pengguna bertanggung jawab atas data pengguna, yang dapat diubah olehnya dan disimpan selama eksekusi program. Penyimpanan berlangsung dalam file terpisah di sepanjang jalur% AppData% \ * Nama aplikasi *. Cakupan Aplikasi memungkinkan Anda mengambil nilai parameter tanpa ditentukan ulang oleh pengguna.
Meski ada niat baik, semuanya menjadi lebih rumit.
- Faktanya, ini adalah file XML yang sama yang ukurannya mulai bertambah lebih cepat dan, akibatnya, menjadi tidak nyaman untuk dibaca.
- Konfigurasi dibaca dari file XML sekali, dan kita perlu memuat ulang aplikasi untuk menerapkan perubahan ke data konfigurasi.
- Kelas yang dihasilkan dari file * .settings ditandai dengan pengubah tersegel, jadi kelas ini tidak dapat diwariskan. Selain itu, file ini dapat diubah, tetapi jika regenerasi terjadi, kami kehilangan semua yang kami tulis sendiri.
- Bekerja dengan data hanya berdasarkan nilai kunci. Untuk mendapatkan pendekatan terstruktur untuk bekerja dengan konfigurasi, kita perlu menerapkannya sendiri.
- Sumber data hanya dapat berupa file, penyedia eksternal tidak didukung.
- Selain itu, kami memiliki faktor manusia - parameter pribadi masuk ke sistem kontrol versi dan menjadi terbuka.
Semua masalah ini tetap ada di .NET Framework hingga hari ini.
Konfigurasi .NET Core
Dalam .NET Core, mereka menata ulang konfigurasi dan membuat semuanya dari awal, menghapus kelas ConfigurationManager statis dan memecahkan banyak masalah yang "sebelumnya". Apa yang baru kita dapatkan? Seperti sebelumnya - tahap menghasilkan data konfigurasi dan tahap penggunaan data ini, tetapi dengan siklus hidup yang lebih fleksibel dan diperpanjang.
Menyiapkan dan mengisi dengan data konfigurasi
Jadi untuk tahap pembuatan data, kita bisa menggunakan banyak sumber, tidak membatasi diri kita hanya pada file. Konfigurasi dilakukan melalui IConfgurationBuilder - basis di mana kita dapat menambahkan sumber data. Paket NuGet tersedia untuk berbagai jenis sumber:
| Format | Metode ekstensi untuk menambahkan sumber ke IConfigurationBuilder | Paket NuGet |
| Json | AddJsonFile | Microsoft.Extensions.Configuration.Json |
| XML | AddXmlFile | Microsoft.Extensions.Configuration.Xml |
| INI | AddIniFile | Microsoft.Extensions.Configuration.Ini |
| Argumen baris perintah | AddCommandLine | Microsoft.Extensions.Configuration.CommandLine |
| Variabel lingkungan | AddEnvironmentVariables | Microsoft.Extensions.Configuration.EnvironmentVariables |
| Rahasia pengguna | AddUserSecrets | Microsoft.Extensions.Configuration.UserSecrets |
| KeyPerFile | AddKeyPerFile | Microsoft.Extensions.Configuration.KeyPerFile |
| Azure KeyVault | AddAzureKeyVault | Microsoft.Extensions.Configuration.AzureKeyVault |
Setiap sumber ditambahkan sebagai lapisan baru dan mengganti parameter dengan kunci yang cocok. Berikut adalah contoh Program.cs yang datang secara default di Template Aplikasi Inti ASP.NET (versi 3.1).
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args).ConfigureWebHostDefaults(webBuilder =>
{ webBuilder.UseStartup<Startup>(); });
Saya ingin fokus pada CreateDefaultBuilder . Di dalam metode ini, kita akan melihat bagaimana konfigurasi awal sumber terjadi.
public static IWebHostBuilder CreateDefaultBuilder(string[] args)
{
var builder = new WebHostBuilder();
...
builder.ConfigureAppConfiguration((hostingContext, config) =>
{
IHostingEnvironment env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
if (env.IsDevelopment())
{
Assembly appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName));
if (appAssembly != null)
{
config.AddUserSecrets(appAssembly, optional: true);
}
}
config.AddEnvironmentVariables();
if (args != null)
{
config.AddCommandLine(args);
}
})
...
return builder;
}
Jadi kita mendapatkan bahwa basis untuk seluruh konfigurasi adalah file appsettings.json; Selanjutnya, jika ada file untuk lingkungan tertentu, maka itu akan memiliki prioritas yang lebih tinggi, dan dengan demikian menimpa nilai-nilai dasar yang cocok. Begitu pula dengan setiap sumber berikutnya. Urutan penambahan mempengaruhi nilai akhir. Secara visual, semuanya terlihat seperti ini:
Jika Anda ingin menggunakan pesanan Anda, maka Anda cukup menghapusnya dan menentukan bagaimana Anda membutuhkannya.
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); })
.ConfigureAppConfiguration((context,
builder) =>
{
builder.Sources.Clear();
//
});
Setiap sumber konfigurasi memiliki dua bagian:
- Implementasi IConfigurationSource. Menyediakan sumber nilai konfigurasi.
- Implementasi IConfigurationProvider. Mengonversi data asli menjadi nilai kunci yang dihasilkan.
Dengan mengimplementasikan komponen ini, kita bisa mendapatkan sumber data kita sendiri untuk konfigurasi. Berikut adalah contoh bagaimana Anda dapat mengimplementasikan mendapatkan parameter dari database melalui Entity Framework.
Cara menggunakan dan mengambil data
Sekarang setelah semuanya jelas dengan pengaturan dan pengisian dengan data konfigurasi, saya mengusulkan untuk melihat bagaimana kita dapat menggunakan data ini dan bagaimana mendapatkannya dengan lebih nyaman. Pendekatan baru untuk mengonfigurasi proyek membuat bias besar terhadap format JSON yang populer, dan ini tidak mengherankan, karena dengan bantuannya kita dapat membangun struktur data apa pun, mengelompokkan data, dan memiliki file yang dapat dibaca pada saat yang bersamaan. Mari kita ambil file konfigurasi berikut sebagai contoh:
{
"Features" : {
"Dashboard" : {
"Title" : "Default dashboard",
"EnableCurrencyRates" : true
},
"Monitoring" : {
"EnableRPSLog" : false,
"EnableStorageStatistic" : true,
"StartTime": "09:00"
}
}
}
Semua data membentuk kamus nilai kunci datar, kunci konfigurasi dibentuk dari seluruh hierarki kunci file untuk setiap nilai. Struktur serupa akan memiliki kumpulan data berikut:
| Fitur: Dasbor: Judul | Dasbor default |
| Fitur: Dashboard: EnableCurrencyRates | benar |
| Fitur: Pemantauan: EnableRPSLog | Salah |
| Fitur: Pemantauan: EnableStorageStatistic | benar |
| Fitur: Pemantauan: Waktu Mulai | 09:00 |
Kita bisa mendapatkan nilainya menggunakan objek IConfiguration . Misalnya, berikut ini cara mendapatkan parameter:
string title = Configuration["Features:Dashboard:Title"];
string title1 = Configuration.GetValue<string>("Features:Dashboard:Title");
bool currencyRates = Configuration.GetValue<bool>("Features:Dashboard:EnableCurrencyRates");
bool enableRPSLog = Configuration.GetValue<bool>("Features:Monitoring:EnableRPSLog");
bool enableStorageStatistic = Configuration.GetValue<bool>("Features:Monitoring:EnableStorageStatistic");
TimeSpan startTime = Configuration.GetValue<TimeSpan>("Features:Monitoring:StartTime");
Dan ini sudah cukup bagus, kami memiliki cara yang baik untuk mendapatkan data yang dikirim ke tipe data yang diperlukan, tetapi entah bagaimana tidak sekeren yang kami inginkan. Jika kami menerima data seperti yang diberikan di atas, maka kami akan berakhir dengan kode berulang dan membuat kesalahan dalam nama kunci. Alih-alih nilai individual, Anda dapat merakit objek konfigurasi lengkap. Mengikat data ke suatu objek melalui metode Bind akan membantu kita dalam hal ini. Contoh pengambilan kelas dan data:
public class MonitoringConfig
{
public bool EnableRPSLog { get; set; }
public bool EnableStorageStatistic { get; set; }
public TimeSpan StartTime { get; set; }
}
var monitorConfiguration = new MonitoringConfig();
Configuration.Bind("Features:Monitoring", monitorConfiguration);
var monitorConfiguration1 = new MonitoringConfig();
IConfigurationSection configurationSection = Configuration.GetSection("Features:Monitoring");
configurationSection.Bind(monitorConfiguration1);
Dalam kasus pertama, kami mengikat dengan nama bagian, dan yang kedua, kami mendapatkan bagian dan mengikat darinya. Bagian ini memungkinkan Anda untuk bekerja dengan tampilan sebagian dari konfigurasi - ini adalah cara Anda mengontrol kumpulan data yang kami kerjakan. Bagian juga digunakan dalam metode ekstensi standar - misalnya, mendapatkan string koneksi menggunakan bagian "ConnectionStrings".
string connectionString = Configuration.GetConnectionString("Default");
public static string GetConnectionString(this IConfiguration configuration, string name)
{
return configuration?.GetSection("ConnectionStrings")?[name];
}
Pilihan - tampilan konfigurasi yang diketik
Membuat objek konfigurasi secara manual dan mengikat data tidaklah praktis, tetapi ada solusi berupa penggunaan Opsi . Opsi digunakan untuk mendapatkan tampilan konfigurasi yang sangat diketik. Kelas tampilan harus publik dengan konstruktor tanpa parameter dan properti publik untuk menetapkan nilai, objek diisi melalui refleksi. Detail lebih lanjut dapat ditemukan di sumbernya .
Untuk mulai menggunakan Options, kita perlu mendaftarkan tipe konfigurasi menggunakan metode ekstensi Configure untuk IServiceCollection yang menunjukkan bagian yang akan kita proyeksikan ke kelas kita.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.Configure<MonitoringConfig>(Configuration.GetSection("Features:Monitoring"));
}
Setelah itu, kita dapat menerima konfigurasi dengan memasukkan dependensi pada antarmuka IOptions, IOptionsMonitor, IOptionsSnapshot. Kita bisa mendapatkan objek MonitoringConfig dari antarmuka IOptions melalui properti Value.
public class ExampleService
{
private IOptions<MonitoringConfig> _configuration;
public ExampleService(IOptions<MonitoringConfig> configuration)
{
_configuration = configuration;
}
public void Run()
{
TimeSpan timeSpan = _configuration.Value.StartTime; // 09:00
}
}
Fitur antarmuka IOptions adalah bahwa dalam wadah injeksi ketergantungan, konfigurasi terdaftar sebagai objek dengan siklus hidup Singleton. Pertama kali sebuah nilai diminta oleh properti Value, sebuah objek diinisialisasi dengan data yang ada selama objek tersebut ada. IOptions tidak mendukung penyegaran data. Ada antarmuka IOptionsSnapshot dan IOptionsMonitor untuk mendukung pembaruan.
IOptionsSnapshot di container DI terdaftar dengan siklus hidup Scoped, yang memungkinkan untuk mendapatkan objek konfigurasi baru berdasarkan permintaan dengan cakupan container baru. Misalnya, dalam satu permintaan web kami akan menerima objek yang sama, tetapi untuk permintaan baru kami akan menerima objek baru dengan data yang diperbarui.
IOptionsMonitor terdaftar sebagai Singleton, dengan satu-satunya perbedaan bahwa setiap konfigurasi diterima dengan data sebenarnya pada saat permintaan. Selain itu, IOptionsMonitor memungkinkan Anda untuk mendaftarkan penangan peristiwa perubahan konfigurasi jika Anda perlu merespons peristiwa perubahan data itu sendiri.
public class ExampleService
{
private IOptionsMonitor<MonitoringConfig> _configuration;
public ExampleService(IOptionsMonitor<MonitoringConfig> configuration)
{
_configuration = configuration;
configuration.OnChange(config =>
{
Console.WriteLine(" ");
});
}
public void Run()
{
TimeSpan timeSpan = _configuration.CurrentValue.StartTime; // 09:00
}
}
Dimungkinkan juga untuk mendapatkan IOptionsSnapshot dan IOptionsMontitor berdasarkan nama - ini diperlukan jika Anda memiliki beberapa bagian konfigurasi yang sesuai dengan satu kelas, dan Anda ingin mendapatkan yang spesifik. Misalnya, kami memiliki data berikut:
{
"Cache": {
"Main": {
"Type": "global",
"Interval": "10:00"
},
"Partial": {
"Type": "personal",
"Interval": "01:30"
}
}
}
Jenis yang akan digunakan untuk proyeksi:
public class CachePolicy
{
public string Type { get; set; }
public TimeSpan Interval { get; set; }
}
Kami mendaftarkan konfigurasi dengan nama tertentu:
services.Configure<CachePolicy>("Main", Configuration.GetSection("Cache:Main"));
services.Configure<CachePolicy>("Partial", Configuration.GetSection("Cache:Partial"));
Kami dapat menerima nilai-nilai sebagai berikut:
public class ExampleService
{
public ExampleService(IOptionsSnapshot<CachePolicy> configuration)
{
CachePolicy main = configuration.Get("Main");
TimeSpan mainInterval = main.Interval; // 10:00
CachePolicy partial = configuration.Get("Partial");
TimeSpan partialInterval = partial.Interval; // 01:30
}
}
Jika Anda melihat kode sumber dari metode ekstensi yang kita gunakan untuk mendaftarkan tipe konfigurasinya, Anda dapat melihat bahwa nama defaultnya adalah Options.Default, yang merupakan string kosong. Jadi kami secara implisit selalu mengirimkan nama untuk konfigurasi.
public static IServiceCollection Configure<TOptions>(this IServiceCollection services, IConfiguration config) where TOptions : class
=> services.Configure<TOptions>(Options.Options.DefaultName, config);
Karena konfigurasi dapat direpresentasikan oleh sebuah kelas, kita juga dapat menambahkan validasi nilai parameter dengan menandai properti menggunakan atribut validasi dari namespace System.ComponentModel.DataAnnotations. Misalnya, kami menetapkan bahwa nilai untuk properti Type harus diisi. Tetapi kita juga perlu menunjukkan ketika mendaftarkan konfigurasi bahwa pada prinsipnya validasi harus terjadi. Ada metode ekstensi ValidateDataAnnotations untuk ini.
public class CachePolicy
{
[Required]
public string Type { get; set; }
public TimeSpan Interval { get; set; }
}
services.AddOptions<CachePolicy>()
.Bind(Configuration.GetSection("Cache:Main"))
.ValidateDataAnnotations();
Keunikan dari validasi tersebut adalah bahwa ini hanya akan terjadi pada saat menerima objek konfigurasi. Hal ini membuat sulit untuk memahami bahwa konfigurasi tidak valid saat aplikasi dimulai. Ada masalah di GitHub untuk masalah ini . Salah satu solusi untuk masalah ini dapat menjadi pendekatan yang disajikan di artikel Menambahkan validasi ke objek konfigurasi yang diketik dengan kuat di ASP.NET Core.
Kekurangan Opsi dan cara menyiasatinya
Konfigurasi melalui Opsi juga memiliki kekurangannya. Untuk menggunakan, kita perlu menambahkan ketergantungan, dan setiap kali mendapatkan objek nilai, kita perlu mengakses properti Value / CurrentValue. Anda dapat memperoleh kode yang lebih bersih dengan mendapatkan objek konfigurasi yang bersih tanpa pembungkus Opsi. Solusi paling sederhana untuk masalah ini mungkin pendaftaran tambahan dalam wadah ketergantungan tipe konfigurasi murni.
services.Configure<MonitoringConfig>(Configuration.GetSection("Features:Monitoring"));
services.AddScoped<MonitoringConfig>(provider => provider.GetRequiredService<IOptionsSnapshot<MonitoringConfig>>().Value);
Solusinya sangat mudah, kami tidak memaksa kode terakhir untuk mengetahui tentang IOptions, tetapi kami kehilangan fleksibilitas untuk tindakan konfigurasi tambahan jika kami membutuhkannya. Untuk mengatasi masalah ini, kita dapat menggunakan pola "Bridge", yang memungkinkan kita mendapatkan lapisan tambahan di mana kita dapat melakukan tindakan tambahan sebelum menerima objek.
Untuk mencapai tujuan ini, kita perlu memfaktor ulang kode contoh saat ini. Karena kelas konfigurasi memiliki batasan dalam bentuk konstruktor tanpa parameter, kita tidak dapat meneruskan objek IOptions / IOptionsSnapshot / IOptionsMontitor ke konstruktor; untuk ini kita akan memisahkan pembacaan konfigurasi dari tampilan akhir.
Misalnya, kita ingin menentukan properti StartTime dari kelas MonitoringConfig dengan representasi string menit dengan nilai "09", yang tidak sesuai dengan format standar.
public class MonitoringConfigReader
{
public bool EnableRPSLog { get; set; }
public bool EnableStorageStatistic { get; set; }
public string StartTime { get; set; }
}
public interface IMonitoringConfig
{
bool EnableRPSLog { get; }
bool EnableStorageStatistic { get; }
TimeSpan StartTime { get; }
}
public class MonitoringConfig : IMonitoringConfig
{
public MonitoringConfig(IOptionsMonitor<MonitoringConfigReader> option)
{
MonitoringConfigReader reader = option.Value;
EnableRPSLog = reader.EnableRPSLog;
EnableStorageStatistic = reader.EnableStorageStatistic;
StartTime = GetTimeSpanValue(reader.StartTime);
}
public bool EnableRPSLog { get; }
public bool EnableStorageStatistic { get; }
public TimeSpan StartTime { get; }
private static TimeSpan GetTimeSpanValue(string value) => TimeSpan.ParseExact(value, "mm", CultureInfo.InvariantCulture);
}
Untuk mendapatkan konfigurasi yang bersih, kita perlu mendaftarkannya di container injeksi dependensi.
services.Configure<MonitoringConfigReader>(Configuration.GetSection("Features:Monitoring"));
services.AddTransient<IMonitoringConfig, MonitoringConfig>();
Pendekatan ini memungkinkan Anda membuat siklus hidup yang benar-benar terpisah untuk pembentukan objek konfigurasi. Dimungkinkan untuk menambahkan validasi data Anda sendiri, atau sebagai tambahan menerapkan tahap dekripsi data jika Anda menerimanya dalam bentuk terenkripsi.
Menjamin keamanan data
Tugas konfigurasi penting adalah keamanan data. Konfigurasi file tidak aman karena datanya disimpan dalam teks yang jelas, yang mudah dibaca; sering kali file berada di direktori yang sama dengan aplikasi. Secara tidak sengaja, Anda dapat memasukkan nilai ke sistem kontrol versi, yang dapat mendeklasifikasi data, tetapi bayangkan jika ini adalah kode publik! Situasinya sangat umum sehingga bahkan ada alat yang siap pakai untuk menemukan kebocoran semacam itu - Gitleaks . Ada artikel terpisah yang memberikan statistik dan variasi data yang diungkapkan.
Seringkali proyek harus memiliki parameter terpisah untuk lingkungan yang berbeda (Rilis / Debug, dll.). Misalnya, sebagai salah satu solusi, Anda dapat menggunakan substitusi nilai akhir menggunakan alat integrasi dan pengiriman berkelanjutan, tetapi opsi ini tidak melindungi data selama pengembangan. Alat Rahasia Pengguna dirancang untuk melindungi pengembang . Ini disertakan dalam .NET Core SDK (3.0.100 dan lebih tinggi). Apa prinsip utama alat ini? Pertama, kita perlu menginisialisasi proyek kita untuk bekerja dengan perintah init.
dotnet user-secrets init
Perintah ini menambahkan elemen UserSecretsId ke file proyek .csproj. Dengan parameter ini, kita mendapatkan penyimpanan pribadi yang akan menyimpan file JSON biasa. Perbedaannya adalah bahwa ini tidak terletak di direktori proyek Anda, jadi itu hanya akan tersedia di komputer saat ini. Path untuk Windows adalah% APPDATA% \ Microsoft \ UserSecrets \ <user_secrets_id> \ secret.json, dan untuk Linux dan MacOS ~ / .microsoft / usersecrets / <user_secrets_id> /secrets.json. Kita dapat menambahkan nilai dari contoh di atas dengan perintah set:
dotnet user-secrets set "Features:Monitoring:StartTime" "09:00"
Daftar lengkap dari perintah yang tersedia dapat ditemukan di dokumentasi.
Keamanan data dalam produksi paling baik dipastikan menggunakan penyimpanan khusus, seperti: AWS Secrets Manager, Azure Key Vault, HashiCorp Vault, Consul, ZooKeeper. Untuk menghubungkan beberapa, sudah ada paket NuGet yang sudah jadi, dan untuk beberapa mudah untuk mengimplementasikannya sendiri, karena ada akses ke REST API.
Kesimpulan
Masalah modern membutuhkan solusi modern. Seiring dengan perpindahan dari monolit ke infrastruktur dinamis, pendekatan konfigurasi juga mengalami perubahan. Ada kebutuhan, terlepas dari lokasi dan jenis sumber data konfigurasi, kebutuhan akan respons yang cepat terhadap perubahan data. Bersama dengan .NET Core, kami mendapatkan alat yang bagus untuk mengimplementasikan semua jenis skenario konfigurasi aplikasi.