حقن التبعية في C#: دليل عملي مع أمثلة

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

مقدمة: ما هو حقن التبعية ولماذا هو ضروري

\n

حقن التبعية (DI) هو أحد أنماط التصميم الرئيسية التي تطبق مبدأ عكس التبعيات (Dependency Inversion Principle) من SOLID. عملياً، هذا يعني أن الكائنات لا تنشئ تبعياتها بنفسها، بل تحصل عليها من الخارج. وهذا يجعل الكود مرناً، قابلاً للاختبار، وذا اقتران ضعيف.

\n

في نظام C# و .NET، أصبح DI هو المعيار الفعلي. بدءاً من .NET Core، تحتوي المنصة على حاوية حقن تبعيات مدمجة تُستخدم في ASP.NET Core والخدمات الخلفية وحتى تطبيقات الكونسول. بدون فهم DI، لا يمكن تصور التطوير الحديث باستخدام C#.

\n

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

\n\n

المشكلة بدون حقن التبعية

\n

لنتخيل سيناريو نموذجياً: فئة OrderService تعالج الطلبات وترسل إشعارات البريد الإلكتروني. بدون DI، يبدو الكود كالتالي:

\n
public class EmailSender\n{\n    public void Send(string to, string subject, string body)\n    {\n        Console.WriteLine($"إرسال بريد إلكتروني إلى {to}: {subject}");\n    }\n}\n\npublic class OrderService\n{\n    private EmailSender _emailSender;\n\n    public OrderService()\n    {\n        // ارتباط صلب - الفئة تنشئ التبعية بنفسها\n        _emailSender = new EmailSender();\n    }\n\n    public void ProcessOrder(int orderId)\n    {\n        // منطق معالجة الطلب...\n        _emailSender.Send("user@example.com", "تم تقديم الطلب", $"طلبك رقم #{orderId} تم استلامه.");\n    }\n}
\n

ما المشكلة؟ OrderService مرتبط بشكل صلب بتنفيذ محدد لـ EmailSender. إذا أردنا استبدال إرسال البريد الإلكتروني بـ SMS أو تسجيل، فسنضطر لتعديل كود OrderService. كما يصبح الاختبار أكثر صعوبة: لا يمكن استبدال EmailSender الحقيقي بكائن وهمي (mock).

\n\n

الحل: حقن التبعيات عبر المُنشئ

\n

الخطوة الأولى نحو DI هي استخراج تجريد (واجهة) وتمرير التبعية عبر المُنشئ. يُسمى هذا حقن المُنشئ (Constructor Injection).

\n\n

الخطوة 1: إنشاء واجهة

\n
public interface INotificationService\n{\n    void Send(string to, string subject, string body);\n}
\n\n

الخطوة 2: تنفيذ الواجهة

\n
public class EmailSender : INotificationService\n{\n    public void Send(string to, string subject, string body)\n    {\n        Console.WriteLine($"تم إرسال البريد الإلكتروني: {subject} -> {to}");\n    }\n}\n\npublic class SmsSender : INotificationService\n{\n    public void Send(string to, string subject, string body)\n    {\n        Console.WriteLine($"تم إرسال SMS: {subject} -> {to}");\n    }\n}
\n\n

الخطوة 3: حقن التبعية عبر المُنشئ

\n
public class OrderService\n{\n    private readonly INotificationService _notificationService;\n\n    // يتم تمرير التبعية من الخارج\n    public OrderService(INotificationService notificationService)\n    {\n        _notificationService = notificationService;\n    }\n\n    public void ProcessOrder(int orderId)\n    {\n        // منطق معالجة الطلب...\n        _notificationService.Send("user@example.com", "تم تقديم الطلب", $"طلبك رقم #{orderId} تم استلامه.");\n    }\n}
\n

الآن OrderService لا يعرف أي تنفيذ محدد يُستخدم. يمكننا توفير EmailSender أو SmsSender أو أي فئة أخرى تنفذ INotificationService. أصبح الكود مرناً وقابلاً للاختبار.

\n\n

استخدام حاوية IoC المدمجة في .NET

\n

إنشاء جميع التبعيات يدوياً ليس مريحاً. في .NET توجد حاوية مدمجة (حاوية IoC) تدير دورة حياة الكائنات تلقائياً. لنلقِ نظرة على مثال لتطبيق كونسول.

\n\n

الخطوة 1: تثبيت الحزمة

المدونات

توصيات الكتب