اگه یه مدتیه داری با .NET کار میکنی، حتماً به اصطلاح Dependency Injection یا به اختصار DI برخوردی. شاید اول نامش یه کم ترسناک به نظر برسه، ولی وقتی مفهومش رو درست بفهمی، میبینی که چقدر منطقی و کاربردیه.
به زبان ساده: Dependency Injection یک الگوی طراحی (Design Pattern) است که به شما کمک میکند وابستگیهای بین کلاسها را به شکل صحیح و قابل نگهداری مدیریت کنید. به جای اینکه یک کلاس، اشیاء مورد نیازش رو خودش بسازه، این اشیاء از بیرون به آن تزریق میشوند.
این مفهوم یکی از ستونهای اصلی اصول SOLID است و در دنیای ASP.NET Core، دات نت ۶، دات نت ۷ و دات نت ۸ به صورت Built-in وجود دارد.
بذار با یه مثال شروع کنیم. فرض کن یه کلاس OrderService داری که برای ارسال ایمیل، به یه کلاس EmailService نیاز داره:
// روش غلط - Tight Coupling
public class OrderService
{
private EmailService _emailService;
public OrderService()
{
_emailService = new EmailService(); // وابستگی سخت!
}
public void PlaceOrder(Order order)
{
// پردازش سفارش
_emailService.SendConfirmation(order);
}
}
این روش چند مشکل جدی داره:
سه روش اصلی برای تزریق وابستگی وجود دارد:
رایجترین و پیشنهادیترین روش. وابستگی از طریق سازنده کلاس تزریق میشود:
// تعریف Interface
public interface IEmailService
{
void SendConfirmation(Order order);
}
// پیادهسازی
public class EmailService : IEmailService
{
public void SendConfirmation(Order order)
{
// ارسال ایمیل واقعی
}
}
// کلاس اصلی با Constructor Injection
public class OrderService
{
private readonly IEmailService _emailService;
public OrderService(IEmailService emailService) // تزریق از بیرون
{
_emailService = emailService;
}
public void PlaceOrder(Order order)
{
_emailService.SendConfirmation(order);
}
}
وابستگی از طریق یک Property عمومی تنظیم میشود. در مواردی که وابستگی اختیاری است استفاده میشود:
public class OrderService
{
public IEmailService EmailService { get; set; }
public void PlaceOrder(Order order)
{
EmailService?.SendConfirmation(order);
}
}
وابستگی مستقیماً به متد پاس داده میشود. برای وابستگیهایی که فقط در یک متد خاص نیاز است مناسب است:
public class OrderService
{
public void PlaceOrder(Order order, IEmailService emailService)
{
emailService.SendConfirmation(order);
}
}
IoC Container (Inversion of Control Container) یا همان DI Container ابزاری است که مدیریت چرخه حیات اشیاء و تزریق وابستگیها را خودکار میکند.
دات نت یک IoC Container Built-in داره که در Microsoft.Extensions.DependencyInjection قرار داره. برای ثبت سرویسها در فایل Program.cs از این روش استفاده میشود:
var builder = WebApplication.CreateBuilder(args);
// ثبت سرویسها
builder.Services.AddScoped();
builder.Services.AddTransient();
builder.Services.AddSingleton();
var app = builder.Build();
app.Run();
یکی از مهمترین مفاهیمی که باید خوب بفهمی، طول عمر (Lifetime) سرویسهاست. اشتباه در این بخش میتواند باگهای بسیار سختی ایجاد کند:
AddSingleton() ثبت میشود.AddScoped() ثبت میشود.AddTransient() ثبت میشود.هرگز یک سرویس Scoped یا Transient را درون یک Singleton تزریق نکن! این خطا «Captive Dependency» نام دارد و باعث میشود سرویس کوتاهعمر، عمری به اندازه Singleton پیدا کند که رفتار نامعلوم و باگهای جدی ایجاد میکند.
بیا یه مثال واقعی از یه سیستم مدیریت محصول ببینیم:
// ۱. تعریف اینترفیسها
public interface IProductRepository
{
Task> GetAllAsync();
Task GetByIdAsync(int id);
Task AddAsync(Product product);
}
public interface IProductService
{
Task> GetProductsAsync();
Task AddProductAsync(ProductDto dto);
}
// ۲. پیادهسازی Repository
public class ProductRepository : IProductRepository
{
private readonly AppDbContext _context;
public ProductRepository(AppDbContext context)
{
_context = context;
}
public async Task> GetAllAsync()
=> await _context.Products.ToListAsync();
public async Task GetByIdAsync(int id)
=> await _context.Products.FindAsync(id);
public async Task AddAsync(Product product)
{
await _context.Products.AddAsync(product);
await _context.SaveChangesAsync();
}
}
// ۳. پیادهسازی Service
public class ProductService : IProductService
{
private readonly IProductRepository _repository;
private readonly ILogger _logger;
public ProductService(
IProductRepository repository,
ILogger logger)
{
_repository = repository;
_logger = logger;
}
public async Task> GetProductsAsync()
{
_logger.LogInformation(“دریافت لیست محصولات”);
return await _repository.GetAllAsync();
}
public async Task AddProductAsync(ProductDto dto)
{
var product = new Product { Name = dto.Name, Price = dto.Price };
await _repository.AddAsync(product);
return true;
}
}
// ۴. ثبت در Program.cs
builder.Services.AddDbContext(options =>
options.UseSqlServer(connectionString));
builder.Services.AddScoped();
builder.Services.AddScoped();
// ۵. استفاده در Controller
[ApiController]
[Route(“api/[controller]”)]
public class ProductsController : ControllerBase
{
private readonly IProductService _productService;
public ProductsController(IProductService productService)
{
_productService = productService;
}
[HttpGet]
public async Task Get()
{
var products = await _productService.GetProductsAsync();
return Ok(products);
}
}
یکی از بزرگترین مزایای DI، تستپذیری بالا است. با استفاده از Mock Objects میتوانید هر سرویس را ایزوله تست کنید:
// Unit Test با استفاده از Moq
public class ProductServiceTests
{
[Fact]
public async Task GetProductsAsync_ShouldReturnProducts()
{
// Arrange
var mockRepo = new Mock();
var mockLogger = new Mock>();
var fakeProducts = new List
{
new Product { Id = 1, Name = “لپتاپ”, Price = 25000000 },
new Product { Id = 2, Name = “موبایل”, Price = 15000000 }
};
mockRepo.Setup(r => r.GetAllAsync())
.ReturnsAsync(fakeProducts);
var service = new ProductService(mockRepo.Object, mockLogger.Object);
// Act
var result = await service.GetProductsAsync();
// Assert
Assert.Equal(2, result.Count());
mockRepo.Verify(r => r.GetAllAsync(), Times.Once);
}
}
یاد گرفتن DI عالیه، ولی اگه سایت یا محصول نرمافزاریتون در گوگل دیده نمیشه، همه این تلاشها نصفهنیمه میمونه. تیم متخصص ما با استراتژیهای سئوی اثباتشده، سایت شما رو به صدر نتایج گوگل میبرند و فروش و مشتریانتون رو چند برابر میکند.
همین حالا برای مشاوره رایگان تماس بگیرید:
📞 09190994063 | 09376846692
وقتی نیاز به کنترل بیشتر بر نحوه ساخت سرویس داری:
builder.Services.AddScoped(serviceProvider =>
{
var config = serviceProvider.GetRequiredService();
var gateway = config[“Payment:Gateway”];
return gateway switch
{
“Zarinpal” => new ZarinpalPaymentService(config),
“IDPay” => new IDPayPaymentService(config),
_ => throw new InvalidOperationException(“درگاه پرداخت نامعتبر”)
};
});
در دات نت ۸، قابلیت جدیدی به نام Keyed Services اضافه شده که ثبت چند پیادهسازی از یک اینترفیس را آسانتر میکند:
// ثبت Keyed Services
builder.Services.AddKeyedScoped(“zarinpal”);
builder.Services.AddKeyedScoped(“idpay”);
// استفاده در Controller
public class PaymentController : ControllerBase
{
private readonly IPaymentService _paymentService;
public PaymentController(
[FromKeyedServices(“zarinpal”)] IPaymentService paymentService)
{
_paymentService = paymentService;
}
}
EmailService، به IEmailService وابسته باش.IServiceProvider در کلاسهای Business یک Anti-Pattern است.// Extension Method برای سازماندهی بهتر
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddApplicationServices(
this IServiceCollection services)
{
services.AddScoped();
services.AddScoped();
services.AddScoped();
return services;
}
public static IServiceCollection AddInfrastructureServices(
this IServiceCollection services)
{
services.AddScoped();
services.AddScoped();
return services;
}
}
// استفاده در Program.cs - تمیز و خوانا
builder.Services.AddApplicationServices();
builder.Services.AddInfrastructureServices();
اگه پروژهات بزرگه و ثبت تکتک سرویسها خستهکنندهست، Scrutor یه کتابخانه فوقالعادهست که به DI Container دات نت قابلیت Assembly Scanning میده:
// نصب: dotnet add package Scrutor
builder.Services.Scan(scan => scan
.FromAssemblyOf()
.AddClasses(classes => classes.AssignableTo())
.AsImplementedInterfaces()
.WithScopedLifetime());
DI Container داخلی دات نت برای اکثر پروژهها کافیه، ولی برای پروژههای خیلی بزرگ، Autofac قابلیتهای بیشتری داره:
رقبای شما هر روز مشتری میگیرند چون در گوگل هستند. سئوی حرفهای یعنی سرمایهگذاری که ماهها و سالها برایتان بازدهی دارد. تیم ما با تجربه واقعی در سئوی فارسی، سایت شما را به صفحه اول گوگل میبرد و زنگخورهای فروشتان را چند برابر میکند.
برای مشاوره رایگان همین الان تماس بگیرید:
📞 09190994063 | 09376846692
Dependency Injection دیگه یه “خوببهدونن” نیست؛ بلکه یک مهارت ضروری برای هر توسعهدهنده دات نت حرفهایه. مزایاش واضحه:
با تمرین مثالهای این مقاله و پیادهسازی DI در پروژههای واقعی، به زودی به یکی از قویترین ابزارهای Clean Architecture مسلط خواهی شد.
IoC (Inversion of Control) یک اصل کلی طراحی است که میگوید کنترل جریان برنامه باید معکوس شود. Dependency Injection یکی از الگوهای پیادهسازی IoC است. به عبارت دیگر، DI یک نوع IoC است، ولی IoC الزاماً DI نیست. روشهای دیگر IoC شامل Service Locator و Factory Method هم هستند.
قانون کلی: اگه سرویس حالت (State) اشتراکی بین Requestها ندارد و Thread-Safe است → Singleton. اگه سرویس به DbContext یا اطلاعات مربوط به یک Request خاص نیاز دارد → Scoped. اگه سرویس باید هر بار تازه ساخته شود و State ندارد → Transient.
بله! در Minimal API (دات نت ۶ به بعد) به راحتی میتوان از DI استفاده کرد. سرویسها مستقیماً به پارامترهای Lambda تزریق میشوند: app.MapGet(“/products”, async (IProductService service) => await service.GetProductsAsync());
از آنجا که Background Service ها به صورت Singleton هستند، نمیتوانید مستقیماً یک Scoped Service مثل DbContext را تزریق کنید. راهحل: از IServiceScopeFactory استفاده کنید تا در هر اجرا یک Scope جدید بسازید و سرویس مورد نظر را از آن دریافت کنید.
تأثیر DI بر عملکرد در دنیای واقعی کاملاً ناچیز است. DI Container دات نت بسیار بهینه است و زمان Resolve کردن سرویسها در حد نانوثانیه است. مزایای طراحی صحیح (کد قابل نگهداری، تستپذیر و مقیاسپذیر) بسیار بیشتر از این هزینه ناچیز ارزش دارد.
بله! کافیه پکیج Microsoft.Extensions.DependencyInjection را نصب کنید و یک ServiceCollection بسازید. یا از Generic Host استفاده کنید که همه زیرساختهای لازم از جمله DI، Logging و Configuration را فراهم میکند.
یک توضیح کامل و جامع. ممنون از زحماتتون. قسمت Captive Dependency واقعاً یه نکته طلایی بود که کمتر جایی به این وضوح بهش اشاره شده. آیا راهی برای تشخیص این نوع خطا در زمان کامپایل یا Early Runtime وجود داره؟
خواهش میکنم حامد جان. Captive Dependency یک خطای رایج و مهم است. متأسفانه، DI Container داخلی دات نت به صورت پیشفرض مکانیزمی برای تشخیص این خطا در زمان کامپایل نداره. اما، بعضی از DI Containerهای Third-Party مثل Simple Injector ابزارهای Validation قویتری دارن که میتونن این نوع مشکلات رو زودتر شناسایی کنن. همچنین ابزارهای Static Code Analyzer میتونن کمک کننده باشن. برای راهنمایی بیشتر میتونید با ما تماس بگیرید: 09190994063 و 09376846692
واقعاً این مقاله رو به همه توسعهدهندگان دات نت توصیه میکنم. از اهمیت DI و اصول SOLID تا نکات کاربردی و Best Practices، همه چیز پوشش داده شده. الان دیگه DI رو بهتر از همیشه درک میکنم.
ممنون از لطف شما مریم جان. هدف ما همین بود که یک منبع جامع و کاربردی برای درک DI ارائه کنیم. درک و استفاده صحیح از DI واقعاً مهارت حیاتی برای توسعهدهندگان حرفهایه. اگه در پروژههاتون نیاز به مشاوره یا خدمات سئو داشتید، میتونید با ما تماس بگیرید: 09190994063 و 09376846692
یک مرجع کامل برای DI در دات نت. دست مریزاد. قسمت Factory Method برای ثبت سرویسها و چگونگی انتخاب درگاه پرداخت مثال کاربردی و خوبی بود. آیا میشه در Factory Method از Keyed Services هم استفاده کرد؟
ممنون رضا جان. بله، کاملاً میشه! شما میتونید Keyed Services رو هم از serviceProvider داخل Factory Method دریافت کنید و بر اساس منطق خودتون، سرویس مناسب رو برگردونید. این به شما انعطافپذیری زیادی میده. برای مشاوره تخصصیتر در این زمینه میتونید با ما تماس بگیرید: 09190994063 و 09376846692
بهترین مقالهای بود که تا به حال در مورد Dependency Injection خوندم. مثال Unit Test با Moq هم عالی بود. همیشه در تست کردن مشکل داشتم، الان میتونم با DI و Mocking خیلی راحتتر تست بنویسم.
خوشحالیم که این مقاله تونسته بهتون در زمینه تستپذیری کمک کنه. DI و Mocking واقعاً دو ابزار قدرتمند برای نوشتن Unit Testهای مؤثر و جداگانه هستند. اگه در این زمینه نیاز به کمک یا آموزش بیشتری داشتید، میتونید با ما تماس بگیرید: 09190994063 و 09376846692
ممنون از مقاله خوبتون. همیشه مفهوم DI برام گنگ بود، اما با این توضیحات و مثالها خیلی بهتر متوجه شدم. مخصوصاً بخش طول عمر سرویسها و Captive Dependency خیلی مفید بود.
خوشحالیم که مقاله براتون مفید بوده. درک صحیح طول عمر سرویسها و اجتناب از Captive Dependency برای جلوگیری از باگهای پیچیده بسیار حیاتیه. اگه سوالی داشتید، میتونید با ما تماس بگیرید: 09190994063 و 09376846692
سلام، مقاله جامع و کاملی بود. Constructor Injection رو همیشه استفاده میکردم ولی نمیدونستم چرا بهترینه. الان دلیلش رو فهمیدم. راستی، در مورد Keyed Services در دات نت 8، چطور میشه داینامیک کلید رو انتخاب کرد؟ مثلاً بر اساس یه پارامتر در URL؟
سلام علی جان، خوشحالیم که مقاله براتون روشنگر بوده. برای انتخاب داینامیک Keyed Services بر اساس پارامتر در URL یا هر شرط دیگه، باید Service Locator رو در داخل متد کنترلتون استفاده کنید و با استفاده از GetKeyedService<TService>(key) سرویس مورد نظر رو دریافت کنید. این یه مورد خاصه که استفاده از Service Locator رو توجیه میکنه. برای جزئیات بیشتر و راهنمایی تخصصی میتونید با ما تماس بگیرید: 09190994063 و 09376846692
مقاله فوقالعادهای بود! همیشه به دنبال یه منبع کامل فارسی در مورد DI بودم. قسمت Extension Method برای ثبت سرویسها و همچنین مقایسه با Autofac بسیار ارزشمند بود. آیا استفاده از Autofac برای پروژههای متوسط هم توصیه میشه؟
ممنون محسن جان. برای پروژههای متوسط، DI Container داخلی دات نت معمولاً کافیه و استفاده از Autofac تنها در صورتی توصیه میشه که به قابلیتهای خاصی مثل Interceptor ها یا سیستم ماژولار پیشرفتهتر نیاز داشته باشید. در غیر این صورت، پیچیدگی اضافه رو به پروژه تحمیل میکنه. برای مشاوره بیشتر میتونید با ما تماس بگیرید: 09190994063 و 09376846692
مقاله بینظیری بود! سوالات متداول هم خیلی کاربردی بودن. خصوصاً سوال در مورد استفاده از DI در Background Service. این یک مشکل رایج برای من بوده. ممنون بابت راهحل IServiceScopeFactory.
خوشحالیم که بخش FAQ و راهحل Background Service براتون مفید بوده. مدیریت Scope در Background Service ها یک چالش رایجه و IServiceScopeFactory راه حل استاندارد برای اون هست. اگه سوالات بیشتری داشتید، میتونید با ما تماس بگیرید: 09190994063 و 09376846692
تشکر فراوان بابت این آموزش عالی. من تازه با ASP.NET Core شروع کردم و DI واقعاً برام یک چالش بود. مثالهای عملی خیلی به درکم کمک کرد. الان میخوام Scrutor رو هم امتحان کنم، به نظرم خیلی کاربردی میاد.
خواهش میکنم فاطمه جان، خوشحالیم که براتون مفید بوده. Scrutor واقعاً برای پروژههای بزرگتر و مدیریت تعداد زیاد سرویسها بسیار کاربردیه و باعث میشه کدتون تمیزتر و قابل نگهداریتر بشه. اگه در پیادهسازی نیاز به راهنمایی داشتید، میتونید با ما تماس بگیرید: 09190994063 و 09376846692