الحقن عبر المُنشئ والواجهات
طبّقوا الحقن عبر المُنشئ مع تصميم قائم على الواجهات لفصل المكوّنات وتمكين اختبار الوحدات.
الحقن عبر المُنشئ والواجهات درس مجاني في C# Academy على CoddyKit. هذا هو الدرس 3 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في C# Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة C# Academy 4 دروس في المجموع.
أساسيات حقن الاعتماديات عبر المُنشئ
يُعد حقن الاعتماديات عبر المُنشئ نمط DI الأكثر شيوعًا. تصرّح الفئة باعتمادياتها باعتبارها معاملات للمُنشئ، وتوفرها الحاوية عند إنشاء المثيل.
وهذا يجعل الاعتماديات صريحة ومطلوبة وواضحة.
تعريف الواجهة
ابدؤوا بواجهة تحدد العقد. ويتيح ذلك تبديل تطبيقات مختلفة، حقيقية أو وهمية أو داخل الذاكرة، من دون تغيير الشيفرة التي تعتمد عليها.
public interface IProductRepository
{
Task<Product?> GetByIdAsync(int id);
Task<IEnumerable<Product>> GetAllAsync();
Task AddAsync(Product product);
}تنفيذ الواجهة
يتولى التطبيق الملموس العمل الفعلي، مثل استدعاءات قاعدة البيانات وطلبات HTTP وغير ذلك. ولا يحتاج المستهلك إلى معرفة التطبيق الذي يحصل عليه.
public class SqlProductRepository : IProductRepository
{
private readonly AppDbContext _db;
public SqlProductRepository(AppDbContext db) => _db = db;
public async Task<Product?> GetByIdAsync(int id)
=> await _db.Products.FindAsync(id);
public async Task<IEnumerable<Product>> GetAllAsync()
=> await _db.Products.ToListAsync();
public async Task AddAsync(Product product)
{
_db.Products.Add(product);
await _db.SaveChangesAsync();
}
}الاستهلاك عبر المُنشئ
تستقبل طبقة الخدمة IProductRepository باعتباره معاملًا للمُنشئ. ولا تعرف شيئًا عن SqlProductRepository، بل تتعامل فقط مع عقد الواجهة.
public class ProductService
{
private readonly IProductRepository _repo;
private readonly ILogger<ProductService> _logger;
public ProductService(
IProductRepository repo,
ILogger<ProductService> logger)
{
_repo = repo;
_logger = logger;
}
public async Task<Product?> GetProductAsync(int id)
{
_logger.LogInformation("Fetching product {Id}", id);
return await _repo.GetByIdAsync(id);
}
}تسجيل سلسلة الاعتماديات
سجّلوا كل خدمة في السلسلة. تحلّ الحاوية رسم الاعتماديات كاملًا تلقائيًا — ProductService → IProductRepository → AppDbContext.
builder.Services.AddDbContext<AppDbContext>(opt =>
opt.UseSqlite("Data Source=app.db"));
builder.Services.AddScoped<IProductRepository, SqlProductRepository>();
builder.Services.AddScoped<ProductService>();الاختبار باستخدام كائن وهمي
بما أن الاعتماديات تُحقن عبر الواجهات، يمكن لاختبارات الوحدة تمرير كائن وهمي أو بديل من دون الوصول إلى قاعدة البيانات. وهذه إحدى أكبر فوائد DI.
// Using NSubstitute or Moq in a unit test
var mockRepo = Substitute.For<IProductRepository>();
mockRepo.GetByIdAsync(1).Returns(new Product { Id = 1, Name = "Widget" });
var logger = Substitute.For<ILogger<ProductService>>();
var svc = new ProductService(mockRepo, logger);
var result = await svc.GetProductAsync(1);
Assert.Equal("Widget", result?.Name);معاملات متعددة للمُنشئ
يمكن أن تحتوي الفئة على معاملات عديدة للمُنشئ. وتحلّ الحاوية جميع هذه المعاملات ما دام كل منها مسجّلًا. حافظوا على تركيز المُنشئات، فوجود عدد كبير جدًا من المعاملات يدل غالبًا على أن الفئة تتولى مسؤوليات كثيرة.
public class OrderService
{
public OrderService(
IOrderRepository orders,
IProductRepository products,
IEmailSender email,
ILogger<OrderService> logger)
{
// all injected by DI container
}
}تجنّب النمط المضاد لمحدّد الخدمة
لا تحقنوا IServiceProvider ثم تستدعوا GetService داخل أساليب منطق الأعمال. فهذا يخفي الاعتماديات ويجعل الاختبار صعبًا. فضّلوا حقن الاعتماديات عبر المُنشئ للحصول على شيفرة صريحة قابلة للاختبار.
// BAD
public void Process(IServiceProvider sp)
{
var repo = sp.GetService<IProductRepository>(); // hidden dep!
}
// GOOD
public class Processor
{
private readonly IProductRepository _repo;
public Processor(IProductRepository repo) => _repo = repo;
}صياغة المُنشئ الأساسي (C# 12)
يقدم C# 12 المُنشئات الأساسية لجميع أنواع الفئات، مما يتيح لكم التصريح بالمعاملات مباشرةً في تعريف الفئة لكتابة شيفرة DI أكثر إيجازًا.
// C# 12 primary constructor
public class ProductService(
IProductRepository repo,
ILogger<ProductService> logger)
{
public async Task<Product?> GetAsync(int id)
{
logger.LogInformation("Getting {Id}", id);
return await repo.GetByIdAsync(id);
}
}تبديل التطبيقات
من أهم مزايا DI القائم على الواجهات إمكانية تبديل التطبيق في سطر واحد من دون تغيير أي شيفرة مستهلكة. ويفيد ذلك عند التبديل بين قواعد البيانات أو موفري البريد أو أعلام الميزات.
// Switch from SQL to in-memory for integration tests
if (environment.IsEnvironment("Test"))
builder.Services.AddScoped<IProductRepository, InMemoryProductRepository>();
else
builder.Services.AddScoped<IProductRepository, SqlProductRepository>();من الواقع العملي: وحدة تحكم مع DI
تستخدم وحدات التحكم في ASP.NET Core حقن الاعتماديات عبر المُنشئ بشكل أصلي. ويحل الإطار جميع المعاملات ويمررها إلى وحدة التحكم.
[ApiController]
[Route("api/products")]
public class ProductsController : ControllerBase
{
private readonly ProductService _svc;
public ProductsController(ProductService svc) => _svc = svc;
[HttpGet("{id}")]
public async Task<IActionResult> Get(int id)
{
var product = await _svc.GetProductAsync(id);
return product is null ? NotFound() : Ok(product);
}
}تحقق سريع
ما الفائدة الأساسية من البرمجة اعتمادًا على الواجهات عند استخدام DI؟
مراجعة: حقن الاعتماديات عبر المُنشئ والواجهات
أهم النقاط:
- صرّحوا بالاعتماديات باعتبارها معاملات للمُنشئ، وستوفرها الحاوية
- عرّفوا الواجهات لعقود الخدمات، واحقنوا الواجهة بدلًا من النوع الملموس
- تتيح الواجهات إنشاء كائنات وهمية بسهولة في اختبارات الوحدة وتبديل التطبيقات
- تقلل المُنشئات الأساسية في C# 12 الشيفرة المكررة في الفئات التي تعتمد بكثافة على DI
- تجنبوا النمط المضاد لمحدّد الخدمة، وحافظوا على الاعتماديات صريحة
الأسئلة الشائعة
هل درس «الحقن عبر المُنشئ والواجهات» مجاني؟
نعم — نص درس «الحقن عبر المُنشئ والواجهات» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة C# Academy، انتقل إلى CoddyKit PRO. تتضمن دورة C# Academy 4 دروس في المجموع.
ماذا ستتعلم في «الحقن عبر المُنشئ والواجهات»؟
طبّقوا الحقن عبر المُنشئ مع تصميم قائم على الواجهات لفصل المكوّنات وتمكين اختبار الوحدات. تتمرن على C# Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ C# Academy؟
لا تُشترط خبرة سابقة. C# Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 3 من أصل 4.
كم من الوقت يستغرق درس «الحقن عبر المُنشئ والواجهات»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس C# Academy هذا؟
نعم. كل درس في C# Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- أساسيات حاوية DI
- مدد حياة الخدمات: Transient وScoped وSingleton
- الحقن عبر المُنشئ والواجهات
- أنماط Factory وOptions