سؤال

لدي حالة شائعة جدًا هنا.ولسنوات لم أجد ما إذا كان ما أفعله صحيحًا وفقًا لمعايير الصناعة. فكر في تطبيق يتصل بقاعدة البيانات، ولكن حيث يتم تمرير سلسلة الاتصال بدلاً من تخزينها في بعض الملفات/الإعدادات كمعلمة سطر أوامر عند بدء التشغيل أو يتم تصفح قاعدة البيانات في وقت بدء تشغيل التطبيق.

حسنًا، يصبح من الضروري حفظ سلسلة الاتصال هذه في مكان ما ضمن نطاق التطبيق.الطريقة الأكثر شيوعًا التي رأيتها يتم القيام بها هي وحدة نمطية أو فئة عامة باستخدام أساليب get/set لحفظ سلسلة الاتصالات.هناك طريقة أخرى للقيام بذلك وهي استخدام Singleton.يمكن لأي من الخيارين DAL الخاص بي الوصول إلى سلسلة الاتصال عندما يحتاج إلى ذلك من خلال طريقة GetConnectionString.

هل هناك طريقة أفضل للقيام بذلك؟

تحديث: ليس لدي ملف تكوين، وحتى لو كان لدي، فسوف أحتاج إلى قراءة سلسلة الاتصال مرة واحدة طوال عمر مثيل التطبيق.هل يمكنك توضيح الجزء "حقنه في أي فئة".

هل كانت مفيدة؟

المحلول

في الحالة العالمية العامة، سواء كانت فئة عالمية أو فردية، يجب تجنبها حيثما أمكن ذلك.

سيكون الحل المثالي هو قيام تطبيقك بتحميل سلسلة الاتصال خارج التكوين و حقن في أي الفئات التي تحتاج إليها.اعتمادا على حجم التطبيق الخاص بك، أ IoC حاوية مثل وحدة أو قلعة وندسور يمكن أن يساعد، ولكنه بالتأكيد ليس جزءًا مطلوبًا من الحل.

إذا لم يكن هذا خيارًا وكنت عالقًا في الحفاظ على الحالة العالمية (بسبب قاعدة التعليمات البرمجية الموجودة أو أي شيء آخر)، فلا أعلم أن هناك فرقًا كبيرًا بين النهجين اللذين اقترحتهما.

تحديث: فقط للتوضيح، انسَ كل الأشياء المتعلقة بحاويات IoC في الوقت الحالي، "Inject" هي مجرد طريقة رائعة لقول "تمرير كمعلمة" (إما إلى مُنشئ الفصل، أو عبر خاصية، أو أي شيء آخر).

بدلاً من الحصول على فئة الوصول إلى البيانات، يجب أن تطلب سلسلة الاتصال (من نوع ما من العمومي أو المفرد)، وتمريرها عبر المُنشئ أو الخاصية.

التحديث رقم 2: أعتقد أنه لا يزال هناك بعض سوء الفهم بشأن ما ينطوي عليه هذا النهج.

يتعلق الأمر أساسًا بما إذا كانت فئة الوصول إلى البيانات الخاصة بك تبدو كما يلي:

public class DataAccessClass
{
    public DataAccessClass()
    {
        _connString = SomeStaticThing.GetConnectionString();
    }
}

أو

public class DataAccessClass
{
    public DataAccessClass(string connString)
    {
        _connString = connString;
    }
}

هؤلاء مقالات (وفي الواقع، العديد من المقالات في تلك المدونة) توضح بالتفصيل عددًا من الأسباب التي تجعل الأخير أفضل من الأول (ليس أقلها أنه يكاد يكون من المستحيل اختبار الوحدة).

نعم عند مكان ما يجب أن يكون هناك شخص ثابت مسؤول عن الاستيلاء على سلسلة الاتصال في المقام الأول، ولكن النقطة المهمة هي أن اعتمادياتك على الأساليب الثابتة تقتصر على تلك البقعة الواحدة (والتي من المحتمل أن تكون طريقتك الرئيسية في العملية من تمهيد تطبيقك)، بدلاً من رشها في قاعدة التعليمات البرمجية بأكملها.

نصائح أخرى

من الجميل أن نرى الكثير من الحلول الإبداعية لمثل هذه المشكلة البسيطة ;-)

حقائق بارزة، من سؤال OP:

  1. يتم تمرير سلسلة الاتصال على سطر الأوامر
  2. قد تحتاج العديد من المكونات الأخرى إلى استخدام سلسلة الاتصال

لذا، لا توجد طريقة للتغلب على استخدام عنصر ثابت;سواء أكان متغيرًا عامًا (يصعب القيام به في .NET، حقًا، بدون فئة متضمنة)، أو فئة ثابتة، أو فئة مفردة لا يهم حقًا.سيكون الحل الأقصر مسارًا هو فئة ثابتة تمت تهيئتها بواسطة فئة البرنامج التي تعالج سطر الأوامر.

سيظل كل حل آخر يتطلب وصولاً ثابتًا إلى سلسلة الاتصال التي تم تمريرها, على الرغم من أنهم قد يخفون ذلك خلف طبقة أو أكثر من المراوغة.

أنا لا أقول أنك لا تريد تزيين الحل الأساسي بشيء أكثر روعة، ولكنه ليس ضروريًا، ولا يلغي الطبيعة الثابتة/العالمية بشكل أساسي لسلسلة الاتصال كما وصفت.

إذا كان كل ما تقوم بتخزينه عبارة عن سلسلة (ربما مع مجموعة من إعدادات التطبيقات العامة الأخرى)، فسأستخدم فئة ثابتة مع مجموعة من الخصائص للاحتفاظ بكل ذلك.يعد العمل المفرد بمثابة المزيد من أعمال التعليمات البرمجية والصيانة، ولكنه عمل غير ضروري في هذه الحالة، نظرًا لأن فئة الخصائص الخاصة بك لن تفعل أي شيء؛مجرد عقد الأشياء.

بالتأكيد هناك طريقة.أولاً، اسرع حقن التبعية والتقنيات المشابهة، وقرأت عنها طبقة الخدمة لأن هذا هو ما تحتاجه هنا.

بقدر ما يتعلق الأمر بطبقة الخدمة، فأنت بحاجة إلى نوع من خدمة التكوين، والتي ستكون الوحدة النمطية التي ستستعلم عنها أجزاء تطبيقك للحصول على معلومات التكوين - سلاسل الاتصال، معرفات URI، وما إلى ذلك:

interface IConfigurationService
    string ConnectionString
    bool IntegratedSecurity
    bool EncryptProfiles

يجب أن يكون الأمر واضحًا جدًا أنه لا يوجد سوى مثيل واحد IConfigurationService في نظامك، ولكن يتم تحقيق ذلك باستخدام حاوية DI المفضلة، وليس باستخدام نمط Singleton.

بعد ذلك، ستحصل جميع خدمات DAL الخاصة بك على مرجع إلى IConfigurationService:

class FooDal : IFooDal
    IConfigurationService ConfigurationService

حتى يتمكنوا الآن من استخدامها IConfigurationService.ConnectionString للحصول على سلسلة الاتصال.

هذا يعتمد.

ضع في اعتبارك أن المفرد:

  • يفرض وجود مثيل واحد بالضبط للفئة، و
  • يوفر الوصول العالمي

توفر العالمية إمكانية الوصول الشامل فقط، ولكنها لا تقدم أي ضمانات بشأن عدد عمليات إنشاء مثيلات.

والخيار الأخير، بالطبع، هو استخدام متغير محلي.وهذا يعني، تمرير معلومات التكوين كمعلمة إلى المنشئ أو حيثما تكون هناك حاجة إليها.

يجب أن يكون الاختيار بين الأولين سهلاً إلى حد ما.هل هي كارثة في حالة وجود حالتين من الفصل؟أود أن أقول ربما لا.قد أرغب في إنشاء فئة التكوين الخاصة بي لتوفير خيارات منفصلة لمكون واحد محدد من التطبيق، أو لتهيئة اختبارات الوحدة الخاصة بي.

هناك حالات قليلة جدًا حيث يمكنك يحتاج لضمان وجود مثيل واحد بالضبط.ولهذا السبب، قد يكون الخيار العالمي خيارًا أفضل من الاختيار الفردي.

الاختيار بين المحلي والعالمي أصعب.من الأفضل عمومًا تجنب الحالة العالمية المتغيرة. غير قابل للتغيير تعد الحالة مشكلة أقل، ولن تؤدي إلى نفس مشكلات المزامنة/التزامن/قابلية التوسع التي ستؤدي إليها الحالة العالمية القابلة للتغيير.

لكن في كثير من الأحيان، قد يكون من الأفضل أن يكون متغيرًا محليًا لتمريره إلى المكونات التي تحتاج إليه.يمكن القيام بذلك يدويًا، ببساطة عن طريق تمريره إلى منشئ الكائن الذي يحتاج إلى الوصول إلى قاعدة البيانات، أو يمكن أن يتم تشغيله تلقائيًا إلى حد ما باستخدام حاوية IoC.

ولكن على أية حال، إذا لم يكن عالميًا، فستصبح التعليمات البرمجية الخاصة بك أكثر عمومية وأكثر قابلية للنقل.إذا كان لديه تبعيات مخفية على الفئات والبيانات العالمية يجب موجودة حتى تعمل التعليمات البرمجية، فلا يمكن استخدامها بسهولة في مشاريع أخرى، أو إذا قمت بإعادة بناء قاعدة التعليمات البرمجية بأكملها أكثر من اللازم.

كما يصبح من الصعب أيضًا اختبار الوحدة، مرة أخرى لأن الكود لن يتم تجميعه ما لم تكن هناك بعض البيانات الخارجية التي نود استبعادها من الاختبار.

كما قالAnton، يجب أن يكون لديك واجهة تعرض شيئًا من هذا القبيل

interface IConfigurationService
    string ConnectionString

ثم عندما يحتاج أحد الفصل الخاص بك إلى سلسلة الاتصال الخاصة بك، فإنك تزوده بتطبيق IConfigurationService عند الإنشاء الذي يحتوي على السلسلة الصالحة.تحتاج إلى العثور على مكان صالح لإنشاء التنفيذ الخاص بك عند بدء تشغيل التطبيق (ربما هو الوقت الذي ستحصل فيه على عنوان قاعدة البيانات الخاصة بك)، وتمريره إلى الفصل الذي يحتاج إليه.

على الرغم من أن الأمر قد يبدو يتطلب الكثير من العمل مقارنة بالمفردة أو العالمية، إلا أن هذا سيؤدي إلى تقليل الاقتران في التعليمات البرمجية الخاصة بك وبالتالي تحسين إمكانية إعادة الاستخدام وجعل اختبار الوحدة أسهل، ويكون الأمر واضحًا جدًا بمجرد إقناع نفسك بأن العوالم شريرة (في الغالب) :)

كما ذكرنا من قبل، هناك حاوية IoC ستوفر إطار العمل للقيام بذلك، ولكن قد يكون ذلك مبالغة في حالتك، بالإضافة إلى أنه من الجيد استخدام النمط بنفسك قبل ترك العمل في "الصندوق السحري"

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top