كل مبرمج يتخيل - حسنًا ، أو قد يرغب في تخيله - هو نفسه كطيار طائرة ، عندما يكون لديك مشروع ضخم ، ولوحة ضخمة من أجهزة الاستشعار والمقاييس والمفاتيح الخاصة به ، والتي يمكنك من خلالها تهيئة كل شيء بسهولة كما ينبغي. حسنًا ، على الأقل لا تعمل لرفع الهيكل يدويًا بنفسك. تعد كل من المقاييس والرسوم البيانية جيدة ، لكنني أريد اليوم أن أخبركم بنفس مفاتيح التبديل والأزرار التي يمكنها تغيير معلمات سلوك الطائرة وتكوينها.
من الصعب التقليل من أهمية التكوينات. يستخدم الجميع أسلوبًا أو آخر في تكوين تطبيقاتهم ، ومن حيث المبدأ ، لا يوجد شيء معقد في ذلك ، ولكن هل الأمر بهذه البساطة؟ أقترح إلقاء نظرة على "قبل" و "بعد" في التكوين وفهم التفاصيل: كيف يعمل ، وما الميزات الجديدة التي لدينا وكيفية استخدامها على أكمل وجه. أولئك الذين ليسوا على دراية بالتكوين في .NET Core سيحصلون على الأساسيات ، وأولئك المألوفون سيحصلون على الطعام للتفكير فيه واستخدام أساليب جديدة في عملهم اليومي.
تكوين Pre-.NET Core
في عام 2002 ، تم تقديم .NET Framework ، وبما أنه كان وقت الضجيج في XML ، قرر المطورون من Microsoft "لنحصل عليه في كل مكان" ، ونتيجة لذلك ، حصلنا على تكوينات XML التي لا تزال حية. في رأس الجدول ، لدينا فئة ConfigurationManager ثابتة نحصل من خلالها على تمثيلات سلسلة لقيم المعلمات. بدا التكوين نفسه كما يلي:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="Title" value=".NET Configuration evo" />
<add key="MaxPage" value="10" />
</appSettings>
</configuration>
تم حل المشكلة ، وحصل المطورون على خيار التخصيص ، وهو أفضل من ملفات INI ، ولكن بخصائصه الخاصة. لذلك ، على سبيل المثال ، يتم تنفيذ دعم قيم الإعدادات المختلفة لأنواع مختلفة من بيئات التطبيق باستخدام تحويلات XSLT لملف التكوين. يمكننا تحديد مخططات XML الخاصة بنا للعناصر والسمات إذا أردنا شيئًا أكثر تعقيدًا من حيث تجميع البيانات. تحتوي أزواج المفتاح والقيمة على نوع سلسلة محدد ، وإذا احتجنا إلى رقم أو تاريخ ، فحينئذٍ "لنفعل ذلك بنفسك بطريقة ما":
string title = ConfigurationManager.AppSettings["Title"];
int maxPage = int.Parse(ConfigurationManager.AppSettings["MaxPage"]);
في عام 2005 أضفنا أقسام التكوين ، حيث سمحوا بتجميع المعلمات ، وبناء مخططاتهم الخاصة ، وتجنب تعارض التسمية. كما قدمنا ملفات * .settings ومصمم خاص لها.
يمكنك الآن الحصول على فئة مُنشأة ومكتوبة بشدة تمثل بيانات التكوين. يسمح لك المصمم بتحرير القيم بسهولة ، كما أن الفرز حسب أعمدة المحرر متاح. يتم استرداد البيانات باستخدام الخاصية الافتراضية للفئة التي تم إنشاؤها ، والتي توفر كائن تكوين Singleton.
DateTime date = Properties.Settings.Default.CustomDate;
int displayItems = Properties.Settings.Default.MaxDisplayItems;
string name = Properties.Settings.Default.ApplicationName;
أضفنا أيضًا نطاقات قيم معلمات التكوين. منطقة المستخدم هي المسؤولة عن بيانات المستخدم ، والتي يمكن تغييرها وحفظها أثناء تنفيذ البرنامج. يتم الحفظ في ملف منفصل على طول المسار٪ AppData٪ \ * Application name *. يسمح لك نطاق التطبيق باسترداد قيم المعلمات دون إمكانية إعادة تعريف المستخدم.
على الرغم من النوايا الحسنة ، أصبح الأمر برمته أكثر تعقيدًا.
- في الواقع ، هذه هي ملفات XML نفسها التي بدأت تنمو في الحجم بشكل أسرع ، ونتيجة لذلك ، أصبحت غير مريحة للقراءة.
- تتم قراءة التكوين من ملف XML مرة واحدة ، ونحتاج إلى إعادة تحميل التطبيق لتطبيق التغييرات على بيانات التكوين.
- تم تمييز الفئات التي تم إنشاؤها من ملفات * .settings باستخدام المُعدِّل المختوم ، لذلك لا يمكن توريث هذه الفئة. بالإضافة إلى ذلك ، يمكن تغيير هذا الملف ، ولكن إذا حدث التجديد ، فإننا نفقد كل ما كتبناه بأنفسنا.
- العمل مع البيانات فقط وفقًا لمخطط قيمة المفتاح. للحصول على نهج منظم للعمل مع التكوينات ، نحتاج إلى تنفيذ ذلك بأنفسنا بشكل إضافي.
- يمكن أن يكون مصدر البيانات ملفًا فقط ، ولا يتم دعم الموفرين الخارجيين.
- بالإضافة إلى ذلك ، لدينا عامل بشري - تدخل المعلمات الخاصة في نظام التحكم في الإصدار وتصبح مكشوفة.
تظل كل هذه المشكلات في .NET Framework حتى يومنا هذا.
تكوين NET Core
في NET Core ، أعادوا تصور التكوين وإنشاء كل شيء من البداية ، وإزالة فئة ConfigurationManager الثابتة وحل العديد من المشكلات التي كانت "من قبل". ما الجديد الذي حصلنا عليه؟ كما كان من قبل - مرحلة إنشاء بيانات التكوين ومرحلة استهلاك هذه البيانات ، ولكن مع دورة حياة أكثر مرونة وإطالة.
إعداد وتعبئة بيانات التكوين
لذلك ، بالنسبة لمرحلة توليد البيانات ، يمكننا استخدام العديد من المصادر ، ولا نقصر أنفسنا على الملفات فقط. يتم التكوين من خلال IConfgurationBuilder - وهو الأساس الذي يمكننا من خلاله إضافة مصادر البيانات. تتوفر حزم NuGet لأنواع مختلفة من المصادر:
| شكل | طريقة الامتداد لإضافة مصدر إلى IConfigurationBuilder | حزمة NuGet |
| جسون | AddJsonFile | Microsoft.Extensions.Configuration.Json |
| XML | AddXmlFile | Microsoft.Extensions.Configuration.Xml |
| INI | AddIniFile | Microsoft.Extensions.Configuration.Ini |
| وسائط سطر الأوامر | AddCommandLine | Microsoft.Extensions.Configuration.CommandLine |
| متغيرات البيئة | إضافة بيئة متغيرات | Microsoft.Extensions.Configuration.EnvironmentVariables |
| أسرار المستخدم | AddUserSecrets | Microsoft.Extensions.Configuration.UserSecrets |
| KeyPerFile | AddKeyPerFile | Microsoft.Extensions.Configuration.KeyPerFile |
| Azure KeyVault | AddAzureKeyVault | Microsoft.Extensions.Configuration.AzureKeyVault |
يُضاف كل مصدر كطبقة جديدة ويتجاوز المعلمات بمفاتيح مطابقة. فيما يلي مثال Program.cs الذي يأتي افتراضيًا في قالب تطبيق ASP.NET Core (الإصدار 3.1).
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args).ConfigureWebHostDefaults(webBuilder =>
{ webBuilder.UseStartup<Startup>(); });
أريد أن أرسم التركيز الرئيسي إلى CreateDefaultBuilder . داخل الطريقة ، سنرى كيف يحدث التكوين الأولي للمصادر.
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;
}
لذلك حصلنا على أن أساس التكوين بأكمله سيكون ملف appsettings.json ؛ علاوة على ذلك ، إذا كان هناك ملف لبيئة معينة ، فسيكون له أولوية أعلى ، وبالتالي يتجاوز القيم المطابقة للقاعدة. وهكذا مع كل مصدر لاحق. ترتيب الإضافة يؤثر على القيمة النهائية. بصريًا ، يبدو كل شيء على النحو التالي:
إذا كنت تريد استخدام طلبك ، فيمكنك ببساطة مسحه وتحديد كيف تريده.
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); })
.ConfigureAppConfiguration((context,
builder) =>
{
builder.Sources.Clear();
//
});
يتكون كل مصدر تكوين من جزأين:
- تنفيذ IConfigurationSource. يوفر مصدرًا لقيم التكوين.
- تنفيذ IConfigurationProvider. يحول البيانات الأصلية إلى القيمة الرئيسية الناتجة.
من خلال تنفيذ هذه المكونات ، يمكننا الحصول على مصدر البيانات الخاص بنا للتكوين. فيما يلي مثال على كيفية تنفيذ الحصول على المعلمات من قاعدة بيانات من خلال Entity Framework.
كيفية استخدام واسترجاع البيانات
الآن بعد أن أصبح كل شيء واضحًا مع الإعداد والتعبئة ببيانات التكوين ، أقترح إلقاء نظرة على كيفية استخدام هذه البيانات وكيفية الحصول عليها بشكل أكثر ملاءمة. النهج الجديد لتكوين المشاريع يشكل تحيزًا كبيرًا تجاه تنسيق JSON الشائع ، وهذا ليس مفاجئًا ، لأنه بمساعدته يمكننا بناء أي هياكل بيانات وبيانات المجموعة والحصول على ملف قابل للقراءة في نفس الوقت. لنأخذ ملف التكوين التالي على سبيل المثال:
{
"Features" : {
"Dashboard" : {
"Title" : "Default dashboard",
"EnableCurrencyRates" : true
},
"Monitoring" : {
"EnableRPSLog" : false,
"EnableStorageStatistic" : true,
"StartTime": "09:00"
}
}
}
تشكل جميع البيانات قاموسًا ثابتًا للقيمة الرئيسية ، ويتكون مفتاح التكوين من التسلسل الهرمي لمفتاح الملف بالكامل لكل قيمة. سيكون لهيكل مماثل مجموعة البيانات التالية:
| الميزات: لوحة القيادة: العنوان | لوحة القيادة الافتراضية |
| الميزات: لوحة القيادة: EnableCurrencyRates | صحيح |
| الميزات: المراقبة: EnableRPSLog | خاطئة |
| الميزات: المراقبة: EnableStorageStatistic | صحيح |
| الميزات: المراقبة: StartTime | 09:00 |
يمكننا الحصول على القيمة باستخدام كائن IConfiguration . على سبيل المثال ، إليك كيفية الحصول على المعلمات:
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");
وهذا جيد بالفعل ، لدينا طريقة جيدة للحصول على البيانات التي يتم إرسالها إلى نوع البيانات المطلوب ، ولكن بطريقة ما ليست رائعة كما نرغب. إذا تلقينا البيانات على النحو الوارد أعلاه ، فسننتهي برمز مكرر ونرتكب أخطاء في أسماء المفاتيح. بدلاً من القيم الفردية ، يمكنك تجميع كائن تكوين كامل. سيساعدنا ربط البيانات بكائن من خلال طريقة Bind في ذلك. مثال على الفئة واسترجاع البيانات:
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);
في الحالة الأولى ، نلتزم باسم القسم ، وفي الحالة الثانية ، نحصل على قسم ونربطه. يسمح لك القسم بالعمل مع عرض جزئي للتكوين - هذه هي الطريقة التي يمكنك بها التحكم في مجموعة البيانات التي نعمل معها. تُستخدم الأقسام أيضًا في طرق الامتداد القياسية - على سبيل المثال ، يستخدم الحصول على سلسلة اتصال قسم "ConnectionStrings".
string connectionString = Configuration.GetConnectionString("Default");
public static string GetConnectionString(this IConfiguration configuration, string name)
{
return configuration?.GetSection("ConnectionStrings")?[name];
}
خيارات - عرض التكوين المكتوب
إن إنشاء كائن تكوين يدويًا والربط بالبيانات ليس عمليًا ، ولكن هناك حل في شكل استخدام خيارات . تُستخدم الخيارات للحصول على عرض مكتوب بقوة للتكوين. يجب أن تكون فئة العرض عامة مع مُنشئ بدون معلمات وخصائص عامة لتعيين قيمة ، ويتم ملء الكائن من خلال الانعكاس. يمكن العثور على مزيد من التفاصيل في المصدر .
لبدء استخدام الخيارات ، نحتاج إلى تسجيل نوع التكوين باستخدام طريقة تكوين الامتداد لمجموعة IServiceCollection التي تشير إلى القسم الذي سنعرضه في الفصل الدراسي لدينا.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.Configure<MonitoringConfig>(Configuration.GetSection("Features:Monitoring"));
}
بعد ذلك ، يمكننا تلقي التكوينات عن طريق إدخال تبعية على واجهات IOptions و IOptionsMonitor و IOptionsSnapshot. يمكننا الحصول على كائن MonitoringConfig من واجهة IOptions من خلال خاصية 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
}
}
تتمثل إحدى ميزات واجهة IOptions في أنه في حاوية حقن التبعية ، يتم تسجيل التكوين ككائن مع دورة حياة Singleton. في المرة الأولى التي يتم فيها طلب قيمة بواسطة خاصية القيمة ، تتم تهيئة كائن ببيانات موجودة طالما كان هذا الكائن موجودًا. لا يدعم IOptions تحديث البيانات. هناك واجهات IOptionsSnapshot و IOptionsMonitor لدعم التحديثات.
يتم تسجيل IOptionsSnapshot في حاوية DI مع دورة الحياة Scoped ، مما يجعل من الممكن الحصول على كائن تكوين جديد عند الطلب مع نطاق حاوية جديد. على سبيل المثال ، أثناء طلب ويب واحد ، سنتلقى نفس الكائن ، ولكن بالنسبة لطلب جديد ، سنتلقى كائنًا جديدًا ببيانات محدثة.
يتم تسجيل IOptionsMonitor على أنه Singleton ، مع الاختلاف الوحيد الذي يتم فيه تلقي كل تكوين مع البيانات الفعلية في وقت الطلب. بالإضافة إلى ذلك ، يسمح لك IOptionsMonitor بتسجيل معالج حدث تغيير التكوين إذا كنت بحاجة إلى الاستجابة لحدث تغيير البيانات نفسه.
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
}
}
من الممكن أيضًا الحصول على IOptionsSnapshot و IOptionsMontitor بالاسم - وهذا ضروري إذا كان لديك عدة أقسام تكوين تتوافق مع فئة واحدة ، وتريد الحصول على قسم محدد. على سبيل المثال ، لدينا البيانات التالية:
{
"Cache": {
"Main": {
"Type": "global",
"Interval": "10:00"
},
"Partial": {
"Type": "personal",
"Interval": "01:30"
}
}
}
النوع المراد استخدامه للإسقاط:
public class CachePolicy
{
public string Type { get; set; }
public TimeSpan Interval { get; set; }
}
نسجل التكوينات باسم محدد:
services.Configure<CachePolicy>("Main", Configuration.GetSection("Cache:Main"));
services.Configure<CachePolicy>("Partial", Configuration.GetSection("Cache:Partial"));
يمكننا الحصول على القيم على النحو التالي:
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
}
}
إذا نظرت إلى الكود المصدري لطريقة الامتداد التي نسجل بها نوع التكوين ، يمكنك أن ترى أن الاسم الافتراضي هو Options.Default ، وهو عبارة عن سلسلة فارغة. لذلك نقوم دائمًا بشكل ضمني بتمرير اسم للتكوينات.
public static IServiceCollection Configure<TOptions>(this IServiceCollection services, IConfiguration config) where TOptions : class
=> services.Configure<TOptions>(Options.Options.DefaultName, config);
نظرًا لأنه يمكن تمثيل التكوين بواسطة فئة ، يمكننا أيضًا إضافة التحقق من صحة قيمة المعلمة عن طريق ترميز الخصائص باستخدام سمات التحقق من مساحة الاسم System.ComponentModel.DataAnnotations. على سبيل المثال ، نحدد أن قيمة خاصية النوع يجب أن تكون مطلوبة. لكننا نحتاج أيضًا إلى الإشارة عند تسجيل التكوين إلى أن التحقق من الصحة يجب أن يحدث من حيث المبدأ. هناك طريقة تمديد ValidateDataAnnotations لهذا الغرض.
public class CachePolicy
{
[Required]
public string Type { get; set; }
public TimeSpan Interval { get; set; }
}
services.AddOptions<CachePolicy>()
.Bind(Configuration.GetSection("Cache:Main"))
.ValidateDataAnnotations();
خصوصية هذا التحقق من الصحة هو أنه سيحدث فقط في لحظة تلقي كائن التكوين. هذا يجعل من الصعب فهم أن التكوين غير صالح عند بدء تشغيل التطبيق. توجد مشكلة على GitHub بخصوص هذه المشكلة . يمكن أن يكون أحد الحلول لهذه المشكلة هو الطريقة المقدمة في المقالة إضافة التحقق من صحة كائنات التكوين المكتوبة بقوة في ASP.NET Core.
عيوب الخيارات وكيفية الالتفاف عليها
التكوين عبر الخيارات له عيوبه أيضًا. للاستخدام ، نحتاج إلى إضافة تبعية ، وفي كل مرة نحتاج إلى الوصول إلى الخاصية Value / CurrentValue للحصول على كائن ذي قيمة. يمكنك تحقيق كود أنظف عن طريق الحصول على كائن تكوين نظيف بدون غلاف الخيارات. قد يكون أبسط حل للمشكلة هو التسجيل الإضافي في حاوية تبعية نوع التكوين الخالص.
services.Configure<MonitoringConfig>(Configuration.GetSection("Features:Monitoring"));
services.AddScoped<MonitoringConfig>(provider => provider.GetRequiredService<IOptionsSnapshot<MonitoringConfig>>().Value);
الحل واضح ومباشر ، فنحن لا نجبر الكود النهائي على معرفة IOptions ، لكننا نفقد المرونة في إجراءات التكوين الإضافية إذا احتجنا إليها. لحل هذه المشكلة ، يمكننا استخدام نمط "Bridge" ، والذي سيتيح لنا الحصول على طبقة إضافية يمكننا من خلالها تنفيذ إجراءات إضافية قبل استلام الكائن.
لتحقيق هذا الهدف ، نحتاج إلى إعادة تشكيل كود المثال الحالي. نظرًا لأن فئة التكوين لها قيود في شكل مُنشئ بدون معلمات ، فلا يمكننا تمرير كائن IOptions / IOptionsSnapshot / IOptionsMontitor إلى المُنشئ ؛ لهذا سنقوم بفصل قراءة التكوين عن العرض النهائي.
على سبيل المثال ، لنفترض أننا نريد تحديد خاصية StartTime لفئة MonitoringConfig مع تمثيل سلسلة من الدقائق بقيمة "09" ، والتي لا تتناسب مع التنسيق القياسي.
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);
}
لكي نتمكن من الحصول على تكوين نظيف ، نحتاج إلى تسجيله في حاوية حقن التبعية.
services.Configure<MonitoringConfigReader>(Configuration.GetSection("Features:Monitoring"));
services.AddTransient<IMonitoringConfig, MonitoringConfig>();
يتيح لك هذا الأسلوب إنشاء دورة حياة منفصلة تمامًا لتشكيل كائن التكوين. من الممكن إضافة التحقق من صحة البيانات الخاصة بك ، أو بالإضافة إلى تنفيذ مرحلة فك تشفير البيانات إذا تلقيتها في شكل مشفر.
ضمان أمن البيانات
مهمة التكوين الهامة هي أمن البيانات. تكوينات الملفات غير آمنة لأن البيانات مخزنة في نص واضح يسهل قراءته ؛ غالبًا ما تكون الملفات في نفس الدليل مثل التطبيق. عن طريق الخطأ ، يمكنك تثبيت القيم في نظام التحكم في الإصدار ، والذي يمكنه إلغاء سرية البيانات ، ولكن تخيل ما إذا كان هذا رمزًا عامًا! الوضع شائع لدرجة أن هناك أداة جاهزة للعثور على مثل هذه التسريبات - Gitleaks . هناك مقال منفصل يقدم الإحصائيات وتنوع البيانات التي تم الكشف عنها.
غالبًا ما يجب أن يحتوي المشروع على معلمات منفصلة لبيئات مختلفة (الإصدار / التصحيح ، إلخ). على سبيل المثال ، كأحد الحلول ، يمكنك استخدام استبدال القيم النهائية باستخدام أدوات التكامل والتسليم المستمر ، لكن هذا الخيار لا يحمي البيانات أثناء التطوير. تم تصميم أداة User Secrets لحماية المطور . تم تضمينه في .NET Core SDK (3.0.100 وما بعده). ما هو المبدأ الرئيسي لهذه الأداة؟ أولاً ، نحتاج إلى تهيئة مشروعنا للعمل باستخدام الأمر init.
dotnet user-secrets init
يضيف الأمر عنصر UserSecretsId إلى ملف المشروع .csproj. باستخدام هذه المعلمة ، نحصل على وحدة تخزين خاصة تخزن ملف JSON عادي. الفرق هو أنه غير موجود في دليل المشروع الخاص بك ، لذلك سيكون متاحًا فقط على الكمبيوتر الحالي. مسار Windows هو٪ APPDATA٪ \ Microsoft \ UserSecrets \ <user_secrets_id> \ secrets.json ، وبالنسبة لنظامي Linux و MacOS ~ / .microsoft / usersecrets / <user_secrets_id> /secrets.json. يمكننا إضافة القيمة من المثال أعلاه باستخدام الأمر set:
dotnet user-secrets set "Features:Monitoring:StartTime" "09:00"
يمكن العثور على قائمة كاملة بالأوامر المتاحة في الوثائق.
من الأفضل ضمان أمان البيانات في الإنتاج باستخدام التخزين المتخصص ، مثل: AWS Secrets Manager و Azure Key Vault و HashiCorp Vault و Consul و ZooKeeper. لتوصيل البعض ، توجد حزم NuGet جاهزة بالفعل ، وبالنسبة للبعض يكون من السهل تنفيذها بنفسك ، نظرًا لوجود إمكانية الوصول إلى REST API.
خاتمة
تتطلب المشاكل الحديثة حلولاً حديثة. إلى جانب الانتقال من البنى التحتية المتجانسة إلى البنى التحتية الديناميكية ، خضعت مناهج التكوين أيضًا لتغييرات. كانت هناك حاجة ، بغض النظر عن الموقع ونوع مصادر بيانات التكوين ، إلى استجابة سريعة لتغييرات البيانات. جنبًا إلى جنب مع .NET Core ، حصلنا على أداة جيدة لتنفيذ جميع أنواع سيناريوهات تكوين التطبيقات.