ليس appsettings.json وحده ── إدارة الإعدادات عمليّاً في تطبيقات Windows للأعمال (الإعدادات حسب البيئة، الأسرار، وجهة الكتابة)
· آخر تحديث: · غو كومورا · CSharp, .NET, appsettings.json, IConfiguration, IOptions, Generic Host, إدارة الإعدادات, تطوير Windows, الاستشارات التقنية
سجل التعديلات (2 تحديثات، آخر تحديث 2 Sep، 2026)
سجل بالتغييرات التي أُجريت على هذا المقال. وحيثما حُفظت نسخة سابقة، تبقى متاحة للقراءة عبر رابط دائم يحمل معرّف DOI.
- أُعيدَت الترجمة العربية كترجمة كاملة عن النص الياباني الأصلي، وأُضيفَت خريطة المعرفة.
- أعيدت الترجمة كترجمة كاملة عن النص الياباني الأصلي. كانت النسخة العربية السابقة مختصراً يسقط أبواباً وجداول ورسوم Mermaid وتعليقات الأشكال وFAQ. أُعيدت هذه العناصر وفق الأصل الياباني، والادّعاءات التقنية مطابقة للنسخة اليابانية.
- النشر الأول
الاستشهاد بهذا المقال(DOI: 10.5281/zenodo.21621650)
هذا المقال محفوظ على Zenodo. يرد أدناه معرّف DOI الذي يشير دائمًا إلى أحدث نسخة، ومعرّف DOI المثبَّت على النسخة التي تقرؤها.
غو كومورا (2026). ليس appsettings.json وحده ── إدارة الإعدادات عمليّاً في تطبيقات Windows للأعمال (الإعدادات حسب البيئة، الأسرار، وجهة الكتابة). شركة كومورا سوفت ذ.م.م.. https://doi.org/10.5281/zenodo.21621650 https://comcomponent.com/ar/blog/dotnet-configuration-management-guide/
- DOI (أحدث نسخة)
- 10.5281/zenodo.21621650
- DOI (هذه النسخة)
- 10.5281/zenodo.22241015
«أريد تغيير سلسلة الاتّصال حسب كلّ بيئة» أو «أريد حفظ إعدادات العرض لكلّ مستخدم» أو «وضعت مفتاح API داخل appsettings.json كما هو». في إدارة الإعدادات لتطبيقات Windows للأعمال، يصل الجميع إلى مرحلة وضع ملفّ appsettings.json واحد والقراءة منه عبر IConfiguration. لكن ما بعد ذلك ── الإعدادات حسب البيئة، وأماكن حفظ الإعدادات القابلة للكتابة، والتعامل مع الأسرار، وتغيير الإعدادات أثناء التشغيل ── مجال كثيراً ما يُترَك دون تنظيم ويدخل حيّز التشغيل كما هو.
يرتب هذا المقال، بالترتيب الذي يتردّد فيه العمل الفعلي، أساس نظام الإعدادات في .NET وهو تراكب IConfiguration والمزوّدات، ثم الإعدادات حسب البيئة، ثم الاستلام الآمن للأنواع عبر نمط IOptions، ثم أماكن الإعدادات القابلة للكتابة، ثم التعامل مع الأسرار، ثم الحدود الواقعيّة لتغيير الإعدادات أثناء التشغيل، ثم خريطة الانتقال من app.config / Settings.settings.
المقال ثمانية فصول وطويل، لذا نبيّن أوّلاً طريقة القراءة بحسب الغرض.
| وضع القارئ | ترتيب القراءة الموصى به |
|---|---|
| ستصمّم إدارة الإعدادات من جديد | الفصل 1 (جدول الحكم) ثم الفصول 2-4 (الأساس، الإعدادات حسب البيئة، نمط الخيارات). الفصول 5-6 تكفي عند ظهور الكتابة في الإعدادات أو الأسرار |
تنتقل من app.config في تطبيق قائم |
الفصل 8 (خريطة الانتقال) ثم الفصل 1 (جدول الحكم) ثم الفصل 2. أسرع أن تفهم ركيزة الوجهة أوّلاً، ثم تغلق البنود واحداً واحداً في جدول المقابلة |
| تحقق في «غيّرت الإعداد لكنّه لا ينعكس» | الفصل 2 (أولويّة المزوّدات) والفصل 7 (حدود reloadOnChange). أغلب الأسباب أحدهما |
| تريد معرفة مكان الأسرار فقط | الفصل 6. اضبط أوّلاً أن آليّة التطوير تختلف عن آليّة الإنتاج |
| تريد معرفة وجهة الحفظ فقط (لكلّ مستخدم / لكلّ جهاز) | الفصل 5 |
1. الخلاصة أوّلاً
حكم إدارة الإعدادات يبدأ بتحديد «مكان الوضع» حسب «نوع الإعداد». نلخّص الصورة الكلّية أوّلاً في جدول حكم.
| نوع الإعداد | مثال ملموس | المرشّح الأوّل لمكان الوضع | السبب |
|---|---|---|---|
| القيم الافتراضيّة للتطبيق | المستوى الافتراضي للسجلّ، معاملات واجهة المستخدم الافتراضيّة | appsettings.json |
قيم مشتركة لا تعتمد على البيئة، تُضمَّن في ناتج البناء |
| إعدادات حسب البيئة | سلسلة الاتّصال ونقطة نهاية API التي تختلف بين بيئة التحقّق وبيئة الإنتاج | appsettings.{Environment}.json + متغيّرات البيئة |
يمكن استخدام ترتيب الكتابة الفوقي الافتراضي كما هو |
| إعدادات لكلّ مستخدم | آخر مجلّد فُتِح، موضع النافذة، إعدادات العرض الشخصيّة | ملفّ خاص تحت %LOCALAPPDATA% (أو %APPDATA%) |
يلزم حيّز قابل للكتابة لكلّ مستخدم |
| إعدادات لكلّ جهاز (مشتركة بين كلّ المستخدمين) | رقم منفذ COM للجهاز، عنوان خادم الترخيص | ملفّ خاص تحت %ProgramData% |
يضبطها المسؤول مرّة لكلّ جهاز ويشاركها كلّ المستخدمين |
| أسرار | كلمة مرور سلسلة الاتّصال، مفتاح API، الرموز | ملفّ محمي بـ DPAPI (الإنتاج)، user-secrets (التطوير فقط) | لا تُوضَع بالنصّ الصريح. لا تُنقل آليّة التطوير إلى الإنتاج |
| قيم تتغيّر أثناء التشغيل | أعلام الميزات، تغيير مستوى السجلّ ديناميكيّاً | appsettings.json (reloadOnChange) + IOptionsMonitor |
تُقصر على القيم التي تريد انعكاسها دون إعادة تشغيل |
بعد هذا الجدول نكتب الخلاصة أوّلاً.
- أولويّة الإعدادات تتبع قاعدة واحدة: «المزوّد المضاف لاحقاً يغلب». في الوضع الافتراضي تُقرأ بالترتيب
appsettings.jsonثمappsettings.{Environment}.jsonثم (بيئة التطوير فقط) user secrets ثم متغيّرات البيئة ثم معطيات سطر الأوامر، وإن وُجد المفتاح نفسه غلبت القيمة المقروءة لاحقاً. حفظ هذا الترتيب وحده يفسّر أغلب حالات «كتبته في json لكنّه لا ينعكس».1 - إذا استخدمت
Host.CreateApplicationBuilder، تُجهَّز هذه الطبقات افتراضيّاً دون أن تركّبها بنفسك. حتّى تطبيقات سطح المكتب مثل Windows Forms/WPF تستفيد من القيم الافتراضيّة نفسها إذا استخدمت Generic Host.23 - تبديل البيئة يتم عبر
DOTNET_ENVIRONMENT(أوASPNETCORE_ENVIRONMENT). في عائلةWebApplicationتُقدَّمDOTNET_ENVIRONMENT، وإن لم يُضبَط أيّ منهما فالافتراضيProduction. في تطبيقات سطح المكتب وخدمات Windows يلزم تصميم «من يمرّر متغيّر البيئة هذا إلى العمليّة وكيف» مسبقاً.4 - لا تستلم الإعدادات كـ DTO، بل كنوع عبر عائلة
IOptions<T>. إن اكتفيت بقيمة عند البدء فاستخدمIOptions<T>، وإن أردت انعكاس التغيير دون إعادة تشغيل فاستخدمIOptionsMonitor<T>. إن استخدمت الاثنين بلا فهم الفرق، تقع حادثتان معاً: «قيمة لا تريد تغيّرها تتغيّر» و«قيمة تريد تغيّرها لا تتغيّر».5 - عيوب الإعداد تُسقِط التطبيق عند البدء لا في وقت التشغيل. إذا جمعت
ValidateDataAnnotations()وValidateOnStart()، صار خطأ الإعداد «يظهر كاستثناء فور البدء»، فتتجنّب حادثة «تكتشف NullReferenceException في منتصف الليل على الإنتاج».6 - لا تضع الأسرار بالنصّ الصريح في
appsettings.json. في التطوير استخدم user-secrets، وفي الإنتاج استخدم DPAPI (ProtectedData) أو مدير بيانات الاعتماد. وثائق Microsoft تنصّ صراحة على أن user-secrets غير مشفّر وهو آليّة للتطوير فقط.78
في المخطّط، يشير الخطّ المتّصل إلى علاقة قائمة دائماً، ويشير الخطّ المتقطّع إلى علاقة مشروطة (شروط قيامها مذكورة في شرح كلّ علاقة في الصفحة التفصيليّة). القائمة الكاملة للعلاقات (المجموع 25، مع الأدلّة ودرجة اليقين) وتعريفات المفاهيم الرئيسة مجمّعة في صفحة تفاصيل خريطة المعرفة (باليابانية). البيانات: JSON-LD / Turtle
2. أساس نظام الإعدادات في .NET ── IConfiguration والمزوّدات
نظام الإعدادات في .NET مبني على آليّة تقرأ عدّة مزوّدات إعدادات متراكبة خلف مظهر مخزن مفتاح-قيمة واحد اسمه IConfiguration. ميزتها أنّ مصادر مختلفة الطابع ── JSON، متغيّرات البيئة، معطيات سطر الأوامر، INI، XML، مجموعات في الذاكرة ── تُدمَج خلف الواجهة نفسها IConfiguration.9
تتراكم المزوّدات بقاعدة بسيطة: «المضاف لاحقاً يغلب». إذا وُجد المفتاح نفسه في أكثر من مزوّد، تسري قيمة آخر مزوّد أُضيف.1
إذا استخدمت Host.CreateApplicationBuilder(args)، تُركَّب المزوّدات افتراضيّاً بالترتيب التالي (الرقم الأكبر أعلى أولويّة، أي يغلب اللاحق).2
appsettings.jsonappsettings.{Environment}.json- Secret Manager (بيئة التطوير فقط)
- متغيّرات البيئة
- معطيات سطر الأوامر
هذا الترتيب الافتراضي مشترك بين تطبيق وحدة التحكّم وWorker Service وتطبيق Windows Forms. فيما يلي مثال بالحدّ الأدنى.
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
// 既定でappsettings.json / appsettings.{Environment}.json / 環境変数 /
// コマンドライン引数が上記の優先順位で読み込まれた状態になっている
string? connectionString = builder.Configuration.GetConnectionString("Main");
using IHost host = builder.Build();
await host.RunAsync();
حتّى تطبيق سطح مكتب مثل Windows Forms يستطيع استخدام HostApplicationBuilder نفسه بعد إضافة حزمة Microsoft.Extensions.Hosting. كما كتبنا في «ما هو .NET Generic Host»، Generic Host أساس يتولّى الإعدادات وDI والسجلّات معاً، ولا مبرّر للتخلّي عن فائدة نظام الإعدادات لأنّ التطبيق لسطح المكتب. لطريقة الدمج الملموسة في تطبيق فيه معالجة مقيمة، راجع أيضاً «إدخال Generic Host / BackgroundService إلى تطبيق سطح المكتب».
لمتغيّرات البيئة نقطة انتباه. إعدادات المضيف (جذر المحتوى واسم البيئة وما شابه) تُقرأ من متغيّرات بيئة ذات بادئة DOTNET_، لكن هذا لا يُستخدم لإعدادات التطبيق (القيم العاديّة المقروءة عبر IConfiguration). متغيّرات البيئة التي تريد قراءتها كإعدادات تطبيق مدمجة في الترتيب الافتراضي بلا بادئة عبر AddEnvironmentVariables().10 إذا أردت بادئة خاصّة، أضفها صراحة مثل builder.Configuration.AddEnvironmentVariables(prefix: "MyApp_").
using Microsoft.Extensions.Configuration;
// 既定のプロバイダーの後に追加されるため、優先度が最も高くなる
builder.Configuration.AddEnvironmentVariables(prefix: "MyApp_");
3. الإعدادات حسب البيئة ── appsettings.{Environment}.json
إذا أردت تغيير سلسلة الاتّصال أو نقطة النهاية حسب البيئة، استخدم ملفّات حسب البيئة مثل appsettings.Development.json / appsettings.Staging.json / appsettings.Production.json. المهمّ أنّ هذا الملف يُعامَل كملفّ فرق «يكتب فوق» appsettings.json الأساسي. لا يلزم إعادة كتابة كلّ البنود؛ يكفي كتابة القيم التي تتغيّر حسب البيئة.1
اسم البيئة يحدّده متغيّر البيئة DOTNET_ENVIRONMENT أو ASPNETCORE_ENVIRONMENT. عند استخدام WebApplication تُقدَّم قيمة DOTNET_ENVIRONMENT على ASPNETCORE_ENVIRONMENT، وإن لم يُضبَط أيّ منهما فالافتراضي Production. Windows لا يميّز حالة الأحرف في أسماء متغيّرات البيئة، أمّا Linux فيميّزها، لذا إن كنت تتطلّع إلى الحاويات فأأمن أن توحّد الإملاء.4
المشكلة هنا أنّ تمرير متغيّر البيئة نفسه عمل قائم بذاته في تطبيقات سطح المكتب وخدمات Windows. آليّة التطوير مثل launchSettings.json في ASP.NET Core لا تُستخدم إلّا في التطوير المحلّي، لذا يلزم تجهيز وسيلة منفصلة لتبديل البيئة في الإنتاج. أشهر طرق التمرير ثلاث.
- ضبطه كمتغيّر بيئة على مستوى الجهاز. إذا ثبّته مثل
setx DOTNET_ENVIRONMENT Production /M، وُرِث لكلّ العمليّات على ذلك الجهاز. لكنّه لا يناسب إن أردت أن تتعايش تطبيقات لبيئات متعدّدة على الجهاز نفسه. - تمرير متغيّر البيئة إلى عمليّة بدء خدمة Windows. إذا أقمت التطبيق كخدمة، بدأ في سياق منفصل عن جلسة المستخدم التفاعلي، فلا تُورَث متغيّرات بيئة المستخدم. لتفاصيل حساب التشغيل وفصل الجلسات راجع «كيفيّة إنشاء خدمات Windows وتشغيلها». الواقعي هو متغيّر بيئة على مستوى الجهاز، أو جعل ملفّ تشغيل الخدمة دفعة غلاف رقيقة تنفّذ
SETثم تشغّل الأصل (محتوى الدفعة أدناه). - عند التشغيل عبر Task Scheduler، أأمن أن تمرّر عبر معطيات سطر الأوامر. لا توجد واجهة في «الإجراء» في Task Scheduler لضبط متغيّرات البيئة مباشرة، لذا يقلّ الحوادث إن مرّرتها كمعطى مثل
--environment Productionوقرأتها بـAddCommandLine(args). عادات حساب التشغيل ونوع تسجيل الدخول الخاصّة بـ Task Scheduler ملخَّصة في «لماذا لا تُنفَّذ مهامّ Task Scheduler أو تنتهي بالرمز 0x1».
دفعة الغلاف المذكورة ثانياً تكفي بثلاثة أسطر.
@echo off
set DOTNET_ENVIRONMENT=Production
"%~dp0MyApp.Service.exe" %*
%~dp0 هو مجلّد الدفعة نفسها (مع \ في النهاية)، لذا تستطيع تشغيل الملفّ التنفيذي المجاور أينما كان مجلّد العمل. set يسري على هذه العمليّة وأبنائها فقط، فلا يتداخل إن تعايشت تطبيقات لبيئات أخرى على الجهاز نفسه. غير أنّه لا يمكن تسجيل هذه الدفعة نفسها كخدمة مباشرة عبر sc create. ما يمكن تسجيله كخدمة هو ملفّ تنفيذي يستجيب لتحكّم الخدمة فقط؛ إن حدّدت دفعة انتهى البدء بمهلة (خطأ 1053). تصلح هذه الطريقة عند التشغيل عبر غلاف خدمة، أو عند التشغيل من Task Scheduler المذكور تالياً. إن أردت تسجيل أصل الخدمة مباشرة، فاختر متغيّر بيئة على مستوى الجهاز أو أسلوب معطيات سطر الأوامر.
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
var options = new HostApplicationBuilderSettings
{
Args = args,
// لمسار تنفيذ لا تُمرَّر فيه متغيرات البيئة (جدولة المهام مثلاً)،
// اسمح أيضاً بتبديل البيئة من وسائط سطر الأوامر
};
HostApplicationBuilder builder = Host.CreateApplicationBuilder(options);
Console.WriteLine($"البيئة الحالية: {builder.Environment.EnvironmentName}");
4. نمط الخيارات ── استلام الإعدادات كنوع
القراءة بمفتاح نصّي مثل IConfiguration["Key:SubKey"] ضعيفة في أنّك لا تكتشف خطأ الإملاء، ويصعب تتبّع التداخل كلّما عمّق. في العمل الفعلي، الأساس هو نمط الخيارات الذي يربط الإعدادات بـ POCO ويستلمها كنوع.
public sealed class ExternalApiOptions
{
public const string SectionName = "ExternalApi";
public required string BaseUrl { get; set; }
public required string ApiKey { get; set; }
public int TimeoutSeconds { get; set; } = 30;
}
التسجيل والربط يُكتَبان كالتالي.
using Microsoft.Extensions.DependencyInjection;
builder.Services
.AddOptions<ExternalApiOptions>()
.Bind(builder.Configuration.GetSection(ExternalApiOptions.SectionName));
للاستلام ثلاث واجهات، ولكلّ منها طابع مختلف.5
| الواجهة | مدّة بقاء التسجيل | انعكاس تغيير الإعداد | الاستخدام الرئيسي |
|---|---|---|---|
IOptions<T> |
مفرد (singleton) | لا ينعكس (يُحسَب مرّة عند البدء) | إعدادات مفترض ثباتها. الأبسط |
IOptionsSnapshot<T> |
نطاق (scoped) | يُعاد الحساب كلّما أُعيد بناء النطاق | سياقات النطاق واضحة مثل نطاق طلب الويب |
IOptionsMonitor<T> |
مفرد (singleton) | تحصل على أحدث قيمة في أيّ وقت مع إشعار التغيير (OnChange) |
سياقات تريد رصد التغيير في مكانه، مثل خدمة مقيمة |
في تطبيق بلا نطاق طلب HTTP، مثل تطبيق سطح المكتب أو خدمة Windows، يتصرّف IOptionsSnapshot<T> عمليّاً مثل IOptions<T> ما لم تنشئ النطاق بنفسك. يقلّ التردّد إن اخترت ببساطة: إن أردت رصد التغيير فاستخدم IOptionsMonitor<T>.
قيم الإعداد (BaseUrl وApiKey والمهلة) تُؤخَذ من IOptionsMonitor في كلّ استدعاء، أمّا HttpClient نفسه فيُؤخَذ من IHttpClientFactory ويُعاد استخدامه. إذا أنشأت HttpClient بـ new وDispose في كلّ استدعاء، هُدِم مجمع المقابس والاتّصالات الداخلي وأُعيد بناؤه في كلّ مرّة، فيؤدّي ذلك في الاستطلاع عالي التواتر أو المعالجة الدفعيّة إلى نفاد المنافذ العابرة (ephemeral ports). القاعدة الثابتة أن تترك إعادة استخدام الاتّصال لـ IHttpClientFactory حتّى إن تغيّرت القيم.
using Microsoft.Extensions.Options;
public sealed class ExternalApiClient(
IHttpClientFactory httpClientFactory,
IOptionsMonitor<ExternalApiOptions> optionsMonitor)
{
public async Task<string> FetchAsync(CancellationToken cancellationToken)
{
// 呼び出しのたびに最新値を取る。設定ファイルが更新されていれば反映される
ExternalApiOptions current = optionsMonitor.CurrentValue;
// HttpClient自体はファクトリ経由で取得し、内部の接続プールを使い回す
HttpClient client = httpClientFactory.CreateClient(nameof(ExternalApiClient));
using var request = new HttpRequestMessage(HttpMethod.Get, new Uri(new Uri(current.BaseUrl), "status"));
request.Headers.Add("X-Api-Key", current.ApiKey);
using var timeoutCts = new CancellationTokenSource(TimeSpan.FromSeconds(current.TimeoutSeconds));
using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCts.Token);
using HttpResponseMessage response = await client.SendAsync(request, linkedCts.Token);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync(cancellationToken);
}
}
في جهة الاستدعاء سجّل عميلاً مسمّى مثل builder.Services.AddHttpClient(nameof(ExternalApiClient));. التقسيم الوظيفي أن تضع على HttpRequestMessage في كلّ طلب القيم التي قد تتغيّر بتغيير الإعداد مثل BaseUrl وApiKey، وأن تُترَك تكلفة إنشاء HttpClient وإتلافه لجهة المصنع.
إذا ظهر خطأ الإعداد كاستثناء في وقت التشغيل طال التحقيق. إذا جمعت التحقّق عبر DataAnnotations وValidateOnStart()، أمكن تصميم تطبيق إعداداته مكسورة لا يبدأ أصلاً.6
using System.ComponentModel.DataAnnotations;
using Microsoft.Extensions.DependencyInjection;
public sealed class ExternalApiOptions
{
public const string SectionName = "ExternalApi";
[Required, Url]
public required string BaseUrl { get; set; }
[Required, MinLength(16)]
public required string ApiKey { get; set; }
[Range(1, 300)]
public int TimeoutSeconds { get; set; } = 30;
}
builder.Services
.AddOptions<ExternalApiOptions>()
.Bind(builder.Configuration.GetSection(ExternalApiOptions.SectionName))
.ValidateDataAnnotations()
.ValidateOnStart(); // ホスト起動時(StartAsync/RunAsync時)、ホストサービス開始前に検証が走る
إن لم تُضِف ValidateOnStart()، تأجّل التحقّق حتّى أوّل وصول فعلي إلى ذلك الخيار. التحقّق نفسه لا يجري فور Build() بل عند بدء المضيف (StartAsync / RunAsync)، فانتبه أنّ شيفرة اختبار استدعت Build() ولم تستدعِ بعد RunAsync() لم يُنفَّذ فيها التحقّق بعد في تلك اللحظة. لتفادي حادثة متقطّعة من نوع «بدأ لكنّه سقط لحظة فتح الشاشة التي تستخدم ذلك الإعداد»، اجعل إعدادات تطبيق الأعمال تحمل ValidateOnStart() من حيث المبدأ.6
5. أين توضع الإعدادات القابلة للكتابة
appsettings.json مكان لوضع «قيم افتراضيّة للقراءة فقط»، وليس مكان إعدادات يعيد التطبيق نفسه كتابتها. كثير من تطبيقات الأعمال يُثبَّت تحت Program Files، ولا يملك المستخدم القياسي صلاحية الكتابة، فتقع حادثة استثناء وقت التشغيل، أو انحراف المحتوى الظاهر لكلّ مستخدم بسبب افتراض نظام الملفّات في Windows.
الإعدادات التي تحتاج كتابة تُقسَم حسب طبيعتها كالتالي. الأأمن أن تعتمد على المجلّدات الخاصّة التي تحصل عليها بـ Environment.GetFolderPath.11
| مكان الوضع | طريقة الحصول | الاستخدام |
|---|---|---|
%LOCALAPPDATA%\اسم الشركة\اسم التطبيق |
Environment.SpecialFolder.LocalApplicationData |
الافتراضي لإعدادات وبيانات كلّ مستخدم |
%APPDATA%\اسم الشركة\اسم التطبيق (Roaming) |
Environment.SpecialFolder.ApplicationData |
إعدادات تريد أن تتبع المستخدم في بيئة الملفّ الشخصي الجوّال فقط |
%ProgramData%\اسم الشركة\اسم التطبيق |
Environment.SpecialFolder.CommonApplicationData |
إعدادات لكلّ جهاز مشتركة بين كلّ المستخدمين. يلزم تصميم ACL |
using System;
using System.IO;
using System.Text.Json;
public sealed class UserSettingsStore
{
private readonly string _filePath;
public UserSettingsStore()
{
string root = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
string dir = Path.Combine(root, "YourCompany", "MyApp");
Directory.CreateDirectory(dir);
_filePath = Path.Combine(dir, "user-settings.json");
}
public UserSettings Load()
{
if (!File.Exists(_filePath))
{
return new UserSettings();
}
string json = File.ReadAllText(_filePath);
return JsonSerializer.Deserialize<UserSettings>(json) ?? new UserSettings();
}
public void Save(UserSettings settings)
{
string json = JsonSerializer.Serialize(settings, new JsonSerializerOptions { WriteIndented = true });
// 同一プロセス内での多重書き込みは呼び出し側で直列化する前提
File.WriteAllText(_filePath, json);
}
}
public sealed class UserSettings
{
public string? LastOpenedFolder { get; set; }
public int WindowWidth { get; set; } = 1024;
public int WindowHeight { get; set; } = 768;
}
إذا خلطت إعدادات كلّ مستخدم وإعدادات كلّ جهاز في ملفّ واحد، وقعت حادثة «إعدادات A تؤذي B» في بيئة يستخدم فيها عدّة مستخدمين الجهاز نفسه. لتمييز كلّ مستخدم / كلّ جهاز ولفهم بنية ملفّ تعريف Windows نفسه راجع «مدخل إلى ملف تعريف المستخدم في Windows»، ولجدول حكم اختيار وجهة الحفظ عموماً راجع «كيف تختار مكان حفظ بيانات تطبيق Windows محليّاً».
6. الأسرار ── سلسلة الاتّصال ومفاتيح API
كتابة كلمة مرور سلسلة الاتّصال أو مفتاح API كما هما في appsettings.json مخاطرة عمليّة حتّى إن أضفت الملف إلى .gitignore. يتسرّب النصّ الصريح عبر مسارات مثل التوزيع بمجلّد مشترك، وإرسال الملف في دعم فنّي، وبقاء ملفّ إعداد قديم «زومبي» في النسخ الاحتياطي.
في التطوير استخدم Secret Manager (dotnet user-secrets). غير أنّ هذه آليّة لتجربة التطوير، وليست مشفّرة. تُحفَظ القيم بالنصّ الصريح JSON في %APPDATA%\Microsoft\UserSecrets\<UserSecretsId>\secrets.json، وتنصّ الوثائق الرسميّة أيضاً على «لا تعاملها كمخزن موثوق، فهي للتطوير فقط». وضع أسرار الإنتاج في user-secrets وتوزيعها استخدام خاطئ.7
dotnet user-secrets init
dotnet user-secrets set "ExternalApi:ApiKey" "開発用の値"
في تشغيل الإنتاج استخدم DPAPI (Data Protection API) الذي يوفّره Windows للتشفير مربوطاً ببيانات اعتماد المستخدم أو الجهاز. Protect / Unprotect في System.Security.Cryptography.ProtectedData غلاف لذلك، وإذا استخدمت DataProtectionScope.CurrentUser أمكن فكّ التشفير فقط عند تسجيل الدخول بالحساب نفسه وعلى الجهاز نفسه. صمّم أيضاً على أنّ DPAPI ميزة خاصّة بـ Windows، وعلى المنصّات الأخرى تصبح PlatformNotSupportedException.8
using System.Security.Cryptography;
using System.Text;
public static class SecretProtector
{
// 用途を示すエントロピーを混ぜておくと、他の目的で保護されたデータと混同しにくくなる
private static readonly byte[] Entropy = Encoding.UTF8.GetBytes("MyApp.ExternalApi.ApiKey");
public static string Protect(string plainText)
{
byte[] plainBytes = Encoding.UTF8.GetBytes(plainText);
byte[] protectedBytes = ProtectedData.Protect(plainBytes, Entropy, DataProtectionScope.CurrentUser);
return Convert.ToBase64String(protectedBytes);
}
public static string Unprotect(string protectedBase64)
{
byte[] protectedBytes = Convert.FromBase64String(protectedBase64);
byte[] plainBytes = ProtectedData.Unprotect(protectedBytes, Entropy, DataProtectionScope.CurrentUser);
return Encoding.UTF8.GetString(plainBytes);
}
}
نكمّل دور الإنتروبيا (optionalEntropy) المستخدمة في الشيفرة أعلاه. نطاق CurrentUser في DPAPI حماية من نوع «يمكن فكّ التشفير إن كان المستخدم نفسه والجهاز نفسه»، لذا إن لم تحدّد إنتروبيا أمكن لتطبيقات أخرى تعمل بصلاحية المستخدم نفسه أن تفكّ التشفير أيضاً. تنصّ الوثائق الرسميّة على أنّه إن مرّرت إنتروبيا عند التشفير، لا يمكن فكّ التشفير ما لم تمرّر القيمة نفسها عند الفكّ.8 أي أنّ الإنتروبيا كلمة سرّ خاصّة بالتطبيق تمنع فكّ التشفير من تطبيقات أخرى تعمل بالمستخدم نفسه وعلى الجهاز نفسه. غير أنّ كلمة السرّ هذه تُضمَّن داخل الملفّ التنفيذي، فافهم أنّها دفاع بمستوى يمكن قراءته بالتحليل (وليست وسيلة تمنع فكّ التشفير من صاحب الحساب نفسه).
تفاصيل مستوى التنفيذ ── أين تحفظ القيمة المحميّة بـ DPAPI، وأيّ نطاق تختار بين CurrentUser وLocalMachine، والمطبّات في تطبيق أعمال يستخدمه عدّة مستخدمين على الجهاز نفسه ── مكتوبة بسمك في «أفضل الممارسات في DPAPI لإبعاد الأسرار عن إعدادات النصّ الصريح في تطبيقات Windows»، فارجع إليها عند التنفيذ.
خطّ الفصل بين ما يجوز وضعه بالنصّ الصريح وما لا يجوز بسيط. احكم بـ «إذا تسرّبت هذه القيمة، هل يلزم إعادة إصدار كلمة المرور أو مفتاح API، والتحقيق في وصول غير مشروع، والإبلاغ للجهة الرقابيّة؟». اسم المضيف ورقم المنفذ في سلسلة الاتّصال كثيراً ما يكون ضرر تسرّبهما صغيراً، أمّا كلمة المرور أو رمز المصادقة المضمَّنان فيها فهما دائماً موضع حماية. إذا صمّمت بناء سلسلة الاتّصال بفصل «معلومات الخادم» عن «بيانات الاعتماد»، وجعلت الأخيرة وحدها موضع حماية DPAPI، أمكن فصل الجزء الذي يبقى ضرره صغيراً بالنصّ الصريح عن الجزء الواجب حمايته على مستوى الشيفرة.
7. تغيير الإعدادات أثناء التشغيل ── واقع reloadOnChange
الاستدعاء الافتراضي لـ AddJsonFile (ما يقوم به Host.CreateApplicationBuilder داخليّاً) يكون reloadOnChange: true، فيرصد appsettings.json / appsettings.{Environment}.json تغيير الملفّ ويعيدان القراءة تلقائيّاً. التنفيذ آليّة يراقب فيها PhysicalFileProvider التغيير داخليّاً عبر FileSystemWatcher.12
لهذه الآليّة حدود عمليّة عدّة.
- ما ينعكس هو القيم المقروءة عبر
IOptionsMonitor<T>(وIOptionsSnapshot<T>) فقط.IOptions<T>يحتفظ بقيمة البدء، لذا تبقى القيمة القديمة حتّى إن عدّلت الملفّ. كثير من استفسارات «عدّلت ملفّ الإعداد مباشرة لكنّه لا ينعكس» سببها الخلط بين الاثنين. - قد لا يرسل
FileSystemWatcherإشعار التغيير بموثوقيّة على أنظمة ملفّات مثل حاوية Docker أو المشاركة الشبكيّة. في تلك البيئات، إذا ضبطت متغيّر البيئةDOTNET_USE_POLLING_FILE_WATCHERعلى1أوtrue، انتقلت المراقبة إلى استطلاع كلّ 4 ثوانٍ (لا يمكن تغيير الفترة).13 علماً أنّ مواصفة «4 ثوانٍ وغير قابلة للتغيير» تأكّدناها من وصف Microsoft Learn «نمط الخيارات - .NET» (تحديث أكتوبر 2025) وقت الكتابة (يوليو 2026). الصفحة لا تصرّح بإصدار، لذا إن وضعتها فرضيّة لتكوين طويل الأمد فراجع أحدث نسخة من الصفحة نفسها. العادات العامّة لـFileSystemWatcherمثل الفقد وفيضان المخزن وتكرار الأحداث ملخَّصة في «دليل عملي لـ FileSystemWatcher». - قد ينطلق إشعار تغيير ملفّ الإعداد عدّة مرّات لتغيير ملفّ واحد. إذا نفّذت في التطبيق «عند رصد التغيير أعد معالجة ثقيلة»، يلزم اعتبار مثل إلغاء الارتداد (debounce) للانطلاقات المتتالية في وقت قصير، أو مقارنة تجزئة محتوى الملفّ لمعالجة التغيّر الجوهري فقط.
لا يلزم أن تسعى دائماً إلى «عكس تغيير الإعداد دون إعادة تشغيل». قيم خفيفة مثل مستوى السجلّ وأعلام الميزات يجوز عكسها فوراً بـ IOptionsMonitor، أمّا قيم قد تناقض موارد تعمل في لحظة التغيير ── مثل سلسلة اتّصال قاعدة البيانات أو حجم مجمع الخيوط ── فأأمن أن تنصّ في المواصفة على «تنعكس بإعادة التشغيل». أهمّ من استخدام reloadOnChange من عدمه أن يكون التصميم قادراً على إبلاغ مسؤول التشغيل بوضوح: «هذا الإعداد يسري فور الحفظ» و«هذا الإعداد يحتاج إعادة تشغيل».
8. خريطة الانتقال من app.config / Settings.settings
عند ترحيل تطبيق من عصر .NET Framework إلى .NET، يلزم أيضاً إعادة بناء آليّة الإعدادات. نرتّب علاقات المقابلة.14
| .NET Framework | .NET | ملاحظة |
|---|---|---|
<appSettings> في App.config / Web.config |
appsettings.json + IConfiguration |
يمكن التعبير عن البنية الهرميّة طبيعيّاً بتداخل JSON |
ConfigurationManager.AppSettings["Key"] |
builder.Configuration["Key"] أو IOptions<T> |
من وصول نصّي إلى وصول مكتوب الأنواع |
ConfigurationManager.ConnectionStrings |
builder.Configuration.GetConnectionString("Name") |
اصطلاح قسم ConnectionStrings ما زال قائماً |
Settings.settings (نطاق المستخدم) |
ملفّ JSON خاص تحت %LOCALAPPDATA% |
لا توجد آليّة توليد تلقائي مثل ApplicationSettingsBase. تسلسل واحفظ بنفسك (مثال صنف الحفظ في الفصل 5) |
Settings.settings (نطاق التطبيق) |
appsettings.json |
عُدّها قيماً افتراضيّة للقراءة فقط |
تشفير <connectionStrings> (aspnet_regiis وما شابه) |
DPAPI (ProtectedData) |
تتغيّر آليّة التشفير نفسها. يلزم إعادة البناء عند الترحيل |
فخّان يُداسان كثيراً عند الترحيل.
الأوّل أنّ إضافة حزمة NuGet System.Configuration.ConfigurationManager تجعل شيفرة قراءة App.config تعمل كما هي. هذه وسيلة صالحة كمرحلة أولى للترحيل، لكن لا تتركها كما هي، بل أدخل الترحيل إلى appsettings.json في الخطّة. مكتبات محيطة مثل مزوّدات السجلّ انتقلت بالجملة إلى افتراض appsettings.json، فيضعف مبرّر الإبقاء على الآليّة القديمة من أجل قراءة App.config وحدها.14
الثاني إعدادات نطاق المستخدم في Settings.settings. في .NET Framework كانت آليّة تحفظ إعدادات كلّ مستخدم تلقائيّاً بمجرّد استدعاء Properties.Settings.Default.Save()، أمّا .NET فلا يوفّر آليّة توليد تلقائي مقابلة في الوضع القياسي. عند الترحيل يلزم تجهيز صنف حفظ خاص كما في الفصل 5.
جانب آخر للترحيل ── توافق المكتبات المعتمدة، ووجود تكامل COM من عدمه، ومراجعة أسلوب التوزيع، وبنود أخرى خارج إدارة الإعدادات ── مرتَّب من زاوية الجرد في «قائمة تحقّق ما قبل ترحيل .NET Framework إلى .NET»، فنوصي بالاطّلاع عليه في المرحلة الأولى لمشروع الترحيل.
الخلاصة
إدارة الإعدادات في .NET مكوّنة من جمع ثلاث آليّات: تراكب المزوّدات عبر IConfiguration، والاستلام الآمن للأنواع عبر عائلة IOptions، وتبديل البيئة عبر متغيّرات البيئة. إلى هنا يمكن لكثير من التطبيقات أن تستخدم الأمر بالطريقة نفسها. أمّا هموم تطبيقات Windows للأعمال فتركّز فيما بعد ذلك: مكان الإعدادات القابلة للكتابة، وحماية الأسرار، وإلى أيّ حدّ تسمح بتغيير الإعدادات أثناء التشغيل، وكيف تطوي أصول جيل app.config.
تقدّم خطوة من حالة «كلّ شيء مكتوب في appsettings.json»، وافصل مكان الوضع والمعاملة حسب نوع الإعداد. نرجو أن يكون جدول الحكم في هذا المقال نقطة انطلاق لذلك الترتيب. جرد إدارة الإعدادات في تطبيق قائم، واستشارة اتّجاه الترحيل من app.config، كثيراً ما لا يظهر الحلّ الأمثل فيهما دون النظر إلى ملفّات الإعداد الفعليّة وبيئة النشر، لذا إن تردّدت فاستشرنا.
مقالات ذات صلة
- ما هو .NET Generic Host
- إدخال Generic Host / BackgroundService إلى تطبيق سطح المكتب
- لماذا لا تُنفَّذ مهامّ Task Scheduler أو تنتهي بالرمز 0x1
- كيفيّة إنشاء خدمات Windows وتشغيلها
- كيف تختار مكان حفظ بيانات تطبيق Windows محليّاً
- مدخل إلى ملف تعريف المستخدم في Windows
- أفضل الممارسات في DPAPI لإبعاد الأسرار عن إعدادات النصّ الصريح في تطبيقات Windows
- دليل عملي لـ FileSystemWatcher
- قائمة تحقّق ما قبل ترحيل .NET Framework إلى .NET
مجالات الاستشارة ذات الصلة
تتعامل شركة كومورا سوفت ذ.م.م. مع استشارات تقنيّة حول تصميم إدارة الإعدادات لتطبيقات Windows للأعمال، وتصميم تشغيل الإعدادات حسب البيئة، واتّجاه الترحيل من أصول app.config القائمة.
روابط مرجعية
-
Microsoft Learn, Configuration in .NET - Alternative hosting approach. حول أولويّة مزوّدات الإعدادات التي يركّبها
Host.CreateApplicationBuilderافتراضيّاً (معطيات سطر الأوامر ثم متغيّرات البيئة ثم user secrets في بيئة التطوير ثم appsettings.{Environment}.json ثم appsettings.json). ↩ ↩2 ↩3 -
Microsoft Learn, .NET Generic Host - Host builder settings. حول الترتيب الافتراضي لإعدادات المضيف التي يقرأها
Host.CreateApplicationBuilder(متغيّرات بيئة ببادئةDOTNET_، ومعطيات سطر الأوامر) وإعدادات التطبيق (appsettings.json، وappsettings.{Environment}.json، وSecret Manager، ومتغيّرات البيئة، ومعطيات سطر الأوامر). ↩ ↩2 -
Microsoft Learn, Use the .NET Generic Host in a Windows Forms app. حول خطوات دمج Generic Host في تطبيق Windows Forms واستخدام DI والإعدادات والسجلّات. ↩
-
Microsoft Learn, ASP.NET Core runtime environments - Environment variables that determine the runtime environment. حول علاقة
DOTNET_ENVIRONMENTوASPNETCORE_ENVIRONMENT، وتقديمDOTNET_ENVIRONMENTعند استخدامWebApplication، وأنّ القيمة الافتراضيّة عند عدم الضبط هيProduction، وأنّ Windows لا يميّز حالة الأحرف في أسماء متغيّرات البيئة بينما Linux يميّزها. ↩ ↩2 -
Microsoft Learn, Options pattern in .NET - Options interfaces. حول فروق مدّة البقاء وتوقيت انعكاس تغيير الإعداد والميزات المقابلة بين
IOptions<TOptions>وIOptionsSnapshot<TOptions>وIOptionsMonitor<TOptions>. ↩ ↩2 -
Microsoft Learn, Options pattern in .NET - Options validation. حول ضبط التحقّق عبر DataAnnotations بـ
ValidateDataAnnotations()، والتحقّق عند البدء بـValidateOnStart()(أوAddOptionsWithValidateOnStart). ↩ ↩2 ↩3 -
Microsoft Learn, Safe storage of app secrets in development in ASP.NET Core - Use the Secret Manager tool. حول أنّ Secret Manager يحفظ الأسرار بلا تشفير كنصّ صريح في
%APPDATA%\Microsoft\UserSecrets\<user_secrets_id>\secrets.json، وأنّه للتطوير فقط ولا ينبغي معاملته كمخزن موثوق. ↩ ↩2 -
Microsoft Learn, ProtectedData Class. حول طريقتي
ProtectedData.Protect/Unprotectاللتين تغلفان DPAPI (Data Protection API)، وفرق النطاق بينDataProtectionScope.CurrentUser/LocalMachine، وأنّه خاص بـ Windows ويصبحPlatformNotSupportedExceptionعلى المنصّات الأخرى. ↩ ↩2 ↩3 -
Microsoft Learn, Configuration providers in .NET. حول آليّة مزوّدات الإعدادات التي تدمج مصادر مختلفة الطابع مثل JSON ومتغيّرات البيئة وسطر الأوامر وINI وXML خلف
IConfiguration. ↩ -
Microsoft Learn, Configuration providers in .NET - Environment variable configuration provider. حول أنّ الإعداد الافتراضي يقرأ متغيّرات البيئة ذات بادئة
DOTNET_ومعطيات سطر الأوامر في إعدادات المضيف وإعدادات التطبيق، وأنّ هذا لا يُستخدم لإعدادات المستخدم، وحول طريقة إضافة بادئة مخصّصة. ↩ -
Microsoft Learn, Environment.GetFolderPath Method. حول الحصول على مسارات المجلّدات الخاصّة عبر تعداد
Environment.SpecialFolderوطريقةGetFolderPath. ↩ -
Microsoft Learn, Detect changes with change tokens in ASP.NET Core - Monitor for configuration changes. حول وسيط
reloadOnChangeفيAddJsonFile، وآليّة مراقبة تغيير ملفّ الإعداد عبرFileSystemWatcherداخلPhysicalFileProvider. ↩ -
Microsoft Learn, Options pattern in .NET - IOptionsMonitor. حول أنّ إشعار تغيير
IOptionsMonitorمقصور على مزوّدات الإعدادات القائمة على نظام الملفّات، وأنّه عند عدم موثوقيّة الإشعار في حاوية Docker أو المشاركة الشبكيّة يمكن التحويل إلى مراقبة استطلاع كلّ 4 ثوانٍ عبر متغيّر البيئةDOTNET_USE_POLLING_FILE_WATCHER. ↩ -
Microsoft Learn, Modernize after upgrading to .NET from .NET Framework - App.config. حول خطوات الترحيل من
App.configإلىappsettings.json، والحفاظ على التوافق عبر حزمة NuGetSystem.Configuration.ConfigurationManager، واستخدام حزمةMicrosoft.Extensions.Configuration.Json. ↩ ↩2
مقالات ذات صلة
أحدث المقالات التي تشترك في نفس الوسوم. عمّق فهمك بمواضيع مرتبطة.
لا تُحِط HttpClient بـ using ── الممارسة العملية لاتصال HTTP في تطبيقات C# للأعمال (أنماط الإنشاء والمهلة وإعادة المحاولة)
إنشاء HttpClient داخل using في كل مرة يستنزف المقابس، وجعله static يمنعه من متابعة تغيّر DNS. نرتّب نمط الإنشاء الصحيح عبر PooledConnecti...
تحديد سبب «البطء» بـ PerfView وdotnet-trace ── مدخل عملي لتحقيق أداء .NET
عندما يصبح تطبيق الأعمال «بطيئاً» أو «يلتصق بالمعالج»، أي أداة تستخدم وماذا تنظر؟ نرتّب توزيع الأدوار بين PerfView وdotnet-trace، وقراءة ...
الطباعة وإخراج PDF في تطبيقات أعمال Windows ── التمييز بين System.Drawing.Printing وWPF ومكتبات التقارير
نرتّب بجدول قرار حسب المتطلبات طباعة WinForms عبر PrintDocument، وطباعة FlowDocument/FixedDocument في WPF، وخيارات إخراج PDF. ونشرح التحك...
انتهاء خدمة برامج تشغيل الطابعات في Windows ── كيف تستعد تطبيقات الأعمال لطباعة التقارير والملصقات
تُنهى Microsoft تدريجيّاً خدمة برامج تشغيل الطابعات v3/v4، ومن تمّوز/يوليو 2026 يُفضَّل برنامج تشغيل فئة IPP. نرتّب في جداول قرار ما يختف...
عندما يُعامَل تطبيق Windows الذي طوّرته شركتك بوصفه فيروساً ── التعامل مع الكشف الخاطئ في Microsoft Defender والتعايش مع أثره على الأداء
نرتّب الإجراء الرسميّ عند كشف Microsoft Defender تطبيق Windows الذي طوّرته شركتك خطأً. نشرح آليّة برامج مكافحة الفيروسات الحديثة، والإبلا...
أين يتصل هذا الموضوع
ترتبط هذه المقالة بشكل طبيعي بصفحات الخدمات التالية.
تطوير تطبيقات ويندوز
لأن إدارة الإعدادات وتصميم ملفّات الإعدادات يدخلان ضمن نطاق الاستشارات العمليّة لتطوير تطبيقات Windows.
الاستشارات التقنية ومراجعة التصميم
لأن تحديد اتّجاه الانتقال من app.config القائم يندرج ضمن استشارة تقنيّة تتضمّن مراجعة تصميم.
الأسئلة الشائعة
أسئلة شائعة حول موضوع هذه المقالة.
- هل يجوز وضع appsettings.json في نفس مجلّد الـ exe؟
- لا مشكلة إن كانت قيماً افتراضيّة للقراءة فقط. لكن إن كان ذلك المجلّد يُثبَّت تحت Program Files، فلا يمكن تصميم التطبيق بحيث يعدّل appsettings.json بنفسه. فالمستخدم القياسي لا يملك صلاحية الكتابة تحت Program Files، ما يؤدّي إمّا إلى استثناء وقت التشغيل، أو إلى اختلاف المحتوى الظاهر لكلّ مستخدم بسبب ميزة الافتراض (virtualization) في Windows. افصل الإعدادات التي تحتاج إلى كتابة في ملفّ منفصل تحت %LOCALAPPDATA% أو %ProgramData%.
- أيّهما ينبغي أن تكون له الأولويّة، متغيّرات البيئة أم appsettings.json؟
- الأساس هو عدم تغيير ترتيب الأولويّة الافتراضي، واستخدام الآليّة كما هي حيث تُكتَب القيم فوق سابقتها بالترتيب: appsettings.json ثم appsettings.{Environment}.json ثم متغيّرات البيئة ثم معطيات سطر الأوامر. ومعيار الحكم عند التردّد هو «طبيعة القيمة». إن جعلت القيم الافتراضيّة التي يجوز تضمينها في ناتج البناء في appsettings.json، والقيم التي تختلف حسب وجهة النشر أو الحاوية في متغيّرات البيئة، فلن يبقى مجال للتردّد لاحقاً.
- كيف ينبغي تقسيم أصناف الإعدادات؟
- الأساس هو تقسيم أصناف الخيارات (options) حسب وحدة الوظيفة أو المسؤوليّة. فمثلاً ConnectionOptions لسلسلة الاتّصال، وExternalApiOptions لتكامل واجهة API الخارجيّة، بحيث لا تُحشَر إعدادات غير مترابطة في صنف واحد. وعند التقسيم، تنفصل بشكل طبيعي أيضاً وحدات التحقّق وإعادة القراءة عبر IOptionsSnapshot/IOptionsMonitor، ويكتمل التحقّق عبر DataAnnotations لكلّ صنف على حدة.
- هل ينبغي الانتقال من ملفّات INI أو الـ Registry؟
- إن كنت تعيد بناء إدارة الإعدادات من الصفر، فالموصى به هو التوحيد على appsettings.json + IConfiguration. ويوفّر .NET أيضاً مزوّد إعدادات (configuration provider) لملفّات INI، لذا يمكن قراءة ملفّات INI القائمة كما هي مع الانتقال تدريجيّاً. أمّا الـ Registry فليس ضمن النطاق القياسي لمزوّدي الإعدادات في .NET، لذا إن وُجدت أصول قائمة تعتمد على الـ Registry، فالحلّ الواقعي هو كتابة شيفرة جسر للقراءة فقط، ثمّ التحوّل تدريجيّاً نحو appsettings.json.
الملف الشخصي للمؤلف
صفحة الملف الشخصي لمؤلف المقالة.
غو كومورا
مؤسّس شركة كومورا سوفت ذ.م.م.
يركّز على تطوير برامج ويندوز، والاستشارات التقنية، والتحقيق في الأخطاء، ويتميّز في المشاريع التي تبقى فيها الأصول القديمة ناشطة، وفي تشخيص الأعطال التي يصعب تحديد سببها.