تزریق وابستگی یکی از الگوهای طراحی پایهای در توسعهی وب با ASP.NET Core و Blazor است. این الگو به توسعهدهندگان کمک میکند تا برنامههایی ماژولار و قابل نگهداری ایجاد کنند، زیرا باعث جداسازی وابستگیها و کاهش اتصال مستقیم میان بخشهای مختلف برنامه میشود.
برای مثال، صفحهای با عنوان Users ممکن است سرویسی به نام UserService را از طریق رابط آن (interface) فراخوانی کند. در زمان اجرا، سیستم تزریق وابستگی، یک نمونه از UserService را هنگام رندر شدن صفحهی Users تزریق میکند.
در این مقاله، بر روی اصول پایهای تزریق وابستگی در Blazor تمرکز خواهیم کرد و بهترین روشها و موارد استفادهای را بررسی میکنیم که فراتر از تزریق سرویسها در صفحات بلیزور هستند. همچنین با یکی از ویژگیهای جدید NET 9 .نیز آشنا خواهیم شد.
مقدمهای بر تزریق وابستگی (Dependency Injection) در Blazor
چندین روش برای تزریق سرویسها به درون کامپوننتهای Razor وجود دارد.
وجه مشترک تمام این روشها این است که ابتدا باید وابستگی مورد نظر را در سیستم تزریق وابستگی ثبت (Register) کنید. معمولاً این کار در فایل Program.cs برنامهی شما که بر پایهی ASP.NET Core ساخته شده (از جمله در برنامههای وب Blazor) انجام میشود.
builder.Services.AddScoped<IUserService, UserService>();
ما از شیء WebApplicationBuilder برای دسترسی به ویژگی Services آن (که از نوع IServiceCollection است) استفاده میکنیم و در آن، پیادهسازی سرویس کاربر (UserService) را برای رابط (IUserService) ثبت میکنیم.
نکته: انواع مختلف چرخهی عمر سرویسها (Service Lifetimes) یا Scopes در ادامهی این مقاله بررسی خواهند شد.
دستور inject
دستور inject به ما این امکان را میدهد که پیادهسازی سرویسها را در فایلهای Razor تزریق کنیم.
@inject IUserService UserServiceاین خط ساده در ابتدای کامپوننت Razor به ما اجازه میدهد متغیری از نوع IUserService تعریف کنیم که در زمان اجرا، یک نمونه (Instance) از آن دریافت خواهد کرد.
این کار باعث میشود آن متغیر هم در بخش الگو (Template) و هم در بخش کد (Code) کامپوننت Razor در دسترس باشد.
این خط ساده در ابتدای کامپوننت Razor به ما اجازه میدهد متغیری از نوع IUserService تعریف کنیم که در زمان اجرا، یک نمونه (Instance) از آن دریافت خواهد کرد.
این کار باعث میشود آن متغیر هم در بخش الگو (Template) و هم در بخش کد (Code) کامپوننت Razor در دسترس باشد.
@code {
[Inject]
private IUserService UserService { get; set; }
}
این خط ساده در بالای کامپوننت Razor به ما اجازه میدهد متغیری از نوع IUserService تعریف کنیم که در زمان اجرا، یک نمونه از آن بهصورت خودکار تزریق میشود.
به این ترتیب، این متغیر هم در بخش الگو (Template) و هم در بخش کد (Code) کامپوننت Razor قابل استفاده خواهد بود.
@code {
private IUserService _userService;
public Home(IUserService userService)
{
_userService = userService;
}
}
نتیجهگیری و استراتژی پیشنهادی برای تزریق سرویسها
سه روش مختلف برای تزریق سرویسها وجود دارد که هر کدام مزایا و معایب خود را دارند.
در برنامههای Blazor از دستور inject@ استفاده کنید،زیرا ساده و مختصر است.
روش تزریق از طریق سازنده (Constructor Injection) نیز گزینهای مطلوب است، زیرا کنترل کاملتری میدهد و از همان سازوکاری استفاده میکند که در دیگر برنامههای #C مانند برنامههای دسکتاپ WPF یا کنترلرهای ASP.NET Core WebAPI وجود دارد.
این روش به توسعهدهندگانی که از محیطهای آشنا وارد Blazor میشوند، کمک میکند تا راحتتر با آن سازگار شوند.
با این حال، هیچ مورد استفادهای نمیبینیم که در آن از تزریق ویژگی (Property Injection) با استفاده از ویژگی [Inject] استفاده شود.
حوزههای تزریق وابستگی (Dependency Injection Scopes) در Blazor
درست مانند برنامههای معمولی ASP.NET Core، در بلیزور نیز سه نوع چرخهی عمر برای سرویسها داریم:
Singleton، Transient و Scoped.
ما هنگام ثبت یک سرویس در مخزن تزریق وابستگی (Dependency Injection Container) — که معمولاً در فایل Program.cs انجام میشود — مشخص میکنیم که کدام چرخهی عمر برای آن سرویس مورد استفاده قرار گیرد.
در ادامه با هر یک از این چرخههای عمر آشنا میشویم و بررسی میکنیم که آیا رفتار آنها در Blazor Server و Blazor WebAssembly (یعنی اجرای سمت سرور یا سمت کاربر) تفاوتی دارد یا خیر.
🔸 Singleton
سرویسهای Singleton تنها یکبار نمونهسازی میشوند و در کل برنامه به اشتراک گذاشته میشوند.
این ویژگی باعث میشود برای نگهداری تنظیمات عمومی برنامه یا وضعیت سراسری (Application-wide State) بسیار مناسب باشند.
از آنجا که فقط یکبار ساخته میشوند، اگر مقداردهی اولیه آنها کمی زمانبر باشد، تأثیر زیادی بر عملکرد کلی برنامه نخواهد داشت.
🔸 Transient
سرویسهای Transient هر بار که تزریق میشوند، یک نمونهی جدید از خود ایجاد میکنند.
به همین دلیل، برای عملیاتهایی که بدون وضعیت (Stateless) هستند و بهسرعت مقداردهی اولیه میشوند، گزینهی مناسبی محسوب میشوند.
اما نباید سرویسهایی را که کد مقداردهی اولیهی سنگین یا کندی دارند بهصورت Transient تعریف کرد، زیرا در این صورت بار اضافی به برنامه وارد میشود.
🔸 Scoped
سرویسهای Scoped در Blazor Server اهمیت ویژهای دارند، زیرا این سرویسها به اتصال SignalR کاربر (Session) وابسته هستند.
به همین دلیل، گزینهی مناسبی برای سرویسهایی هستند که باید دادههای خاص هر کاربر را نگه دارند یا به نوعی با کاربر احراز هویتشده (Authenticated User) در ارتباط هستند.
در واقع، در Blazor Server میتوان Scoped را معادل با Singleton برای هر کاربر (Singleton-per-User) در نظر گرفت.
Blazor Server در مقابل Blazor WebAssembly
در حالت Blazor WebAssembly، اجرای برنامه یا کامپوننتهای Blazor در سمت کاربر (Client-side) انجام میشود و هیچ اتصال SignalR میان سرور و مرورگر وجود ندارد.
به این معنا که برای هر مرورگر، یک نمونهی جداگانه از سرویسهای Singleton و Scoped ایجاد میشود.
ثبت یک سرویس بهصورت Singleton در Blazor WebAssembly، باعث اشتراکگذاری اطلاعات بین کاربران مختلف نمیشود، در حالیکه در Blazor Server این اشتراکگذاری بین تمام کاربران برقرار است.
دامنهی Transient در هر دو مدل Blazor Server و Blazor WebAssembly یکسان است؛ یعنی برای هر بار تزریق، یک نمونهی جدید از سرویس ساخته میشود.
درک تفاوتهای ظریف میان این چرخههای عمر سرویسها، به استفادهی بهینه از منابع کمک میکند و از بروز رفتارهای ناخواسته در برنامه جلوگیری خواهد کرد.
استفاده از تزریق وابستگی (DI) برای مدیریت وضعیت، سرویسهای داده و تست کامپوننتها
تزریق وابستگی در برنامههای Blazor نقش بسیار مهمی در مدیریت وضعیت (State Management)، بازیابی دادهها (Data Retrieval) و تست کامپوننتها (Component Testing) ایفا میکند.
در زمینهی مدیریت وضعیت، تزریق وابستگی به سرویسها اجازه میدهد تا دادههای مشترک را نگهداری و مدیریت کنند. این روش در بسیاری از سناریوها کارآمد است و نیاز به الگوهای پیچیدهی رویدادمحور (Event-driven) را از بین میبرد.
تزریق وابستگی باعث میشود مصرفکننده (Consumer) از تولیدکننده (Producer) جدا شود، و همین موضوع باعث قابلیت تستپذیری (Testability) و قابلیت استفاده مجدد (Reusability) بهتر در برنامه میشود.
برای مثال، اگر یک رفتار خاص را در قالب یک سرویس پیادهسازی کنید، میتوانید همان سرویس را در چندین کامپوننت Razor تزریق کرده و از آن در مکانهای مختلف با عملکرد یکسان استفاده کنید.
همچنین بهتر است منطق مربوط به دسترسی به دادهها (Data Access Logic) را مستقیماً در داخل کامپوننتهای Razor پیادهسازی نکنید. در عوض، آن منطق را در سرویسهای داده (Data Services) پیادهسازی کنید تا بتوانید از آن برای بارگذاری و ذخیرهی دادهها بهره ببرید. این کار دوباره به افزایش تستپذیری، نگهداری آسانتر و استفادهی مجدد از کد کمک میکند.
با استفادهی مؤثر از تزریق وابستگی (Dependency Injection)، میتوانیم برنامههای Blazor را مقیاسپذیر (Scalable)، قابل نگهداری (Maintainable) و قابل تست (Testable) بسازیم.
بهترین روشها برای استفاده از تزریق وابستگی (Dependency Injection) در Blazor
بهکارگیری نکات زیر به شما کمک میکند تا برنامههای Blazor را به شکلی پایدار (Robust)، قابل نگهداری (Maintainable) و قابل استفاده مجدد (Reusable) پیادهسازی کنید:
🔹 سرویسها را با چرخهی عمر مناسب ثبت کنید
در هنگام ثبت سرویسها، چرخهی عمر مناسب را انتخاب کنید (Singleton، Transient یا Scoped).
انتخاب نادرست ممکن است باعث اشتراک ناخواستهی دادهها یا مصرف بیمورد حافظه شود.
🔹 برای سرویسها از رابط (Interface) استفاده کنید
برای هر سرویس، یک رابط (Interface) تعریف کنید تا قابلیت تستپذیری (Testability) و انعطافپذیری (Flexibility) برنامه افزایش یابد.
🔹 از استراتژی جداسازی (Decoupling) درست استفاده کنید
اگر رفتار برنامه را بیش از حد به سرویسهای کوچک تقسیم کنید، در نهایت با تعداد زیادی وابستگی پراکنده مواجه میشوید.
در مقابل، اگر همه چیز را در یک سرویس قرار دهید، کل برنامه به آن وابسته خواهد شد.
بهترین راه، انتخاب یک تعادل منطقی (Sensible Middle Ground) است که هم سادگی را حفظ کند و هم وابستگیها را کاهش دهد.
🔹 از تزریق وابستگی برای پیکربندی برنامه استفاده کنید
به جای دسترسی مستقیم به پیکربندیها در داخل کامپوننتها، یک سرویس پیکربندی (Configuration Service) پیادهسازی کنید که تنظیمات را اعتبارسنجی (Validate) کرده و ساختارهای دادهی مناسب را در اختیار سایر بخشهای برنامه قرار دهد.
بهکارگیری این چهار روش برتر، به شما کمک میکند تا برنامههای Blazor خود را به شکلی قابل نگهداریتر (Maintainable) و قابل گسترشتر (Extensible) پیادهسازی کنید.
نتیجهگیری
تزریق وابستگی (Dependency Injection) یکی از الگوهای طراحی بسیار مهم در توسعهی وب با ASP.NET Core و بهویژه در Blazor است.
این الگو ساده است و یادگیری آن زمان زیادی نمیبرد، اما درک دقیق جزئیاتی مانند چرخهی عمر سرویسها (Service Lifetimes) و رعایت بهترین روشها (Best Practices) برای ساخت برنامههای پایدار (Robust)، قابل نگهداری (Maintainable) و منعطف (Flexible) در Blazor، کاملاً حیاتی است.
برای افزودن دیدگاه خود، نیاز است ابتدا وارد حساب کاربریتان شوید