آقای نصیری یک متن در این مورد دارند که لینکش رو ارسال کردند .
در مورد میان افزارها ، قسمت 7 مقاله هست که به خاطر مشغلهی کاری هنوز اون رو ننوشتم ... اگر وقت بود در مورد واکشی سرویسها و مکانیزمهای واکشی سرویسها هم در ادامه توضیح خواهم داد .
میان افزار / Middle wareها :
این کلاسها فقط و فقط در هنگام تعریف در متد Configure ساخته میشوند و شی ایجاد شده از هر سرویسی که درون سازندهی آنها ثبت بشه ، تا پایان طول حیات برنامه ، در حافظه نگه داشته میشود و اصطلاحا Capture میشود .
مثلا اگر میان افزاری برای لاگ کردن آخرین فعالیت کاربر بنویسیم و UserRepository را در سازنده تعریف کنیم ، یک نمونه از این Repository تا در طول حیات برنامه در حافظه نگه داشته میشود که معمولا باعث اشغال حافظه ، باز نگه داشتن Connection به پایگاه داده و ایجاد خطا میشود . برای جلوگیری از این مشکل ، در میان افزارها ، ما سرویسهای Transient و Scoped را در خود متدهای InvokeAsync و یا Invoke ثبت میکنیم تا با هر درخواست جدید یک نمونهی جدید از آنها ساخته شود و با پایان درخواست نمونهی ساخته شده به درستی از بین برود .
در مورد Validation :
به صورت معمول ، Validation در هر صفحه به ازای هر درخواست ، از ابتدا انجام میشود و بنابراین روش منطقی ثبت و واکشی سرویسهای Validation به صورت Scoped هست تا خطر به اشتراک گذاشتن وضعیت شی وجود نداشته باشد . توصیهی من ثبت این سرویس به صورت Scoped و یا حتی برای امنیت بیشتر ، ثبت آن به صورت Transient هست تا مطمئن شوید که با هر بار فراخوانی این سرویس در طول یک درخواست ، یک نمونهی جدید ساخته و استفاده میشود .
"سرویسهای Scoped در محدودهی درخواست، مانند Singleton عمل میکنند و شیء ساخته شده و وضعیت آن در بین تمامی سرویسهایی که به آن نیاز دارند، مشترک است. بنابراین باید به این نکته در هنگام تعریف سرویس به صورت Scoped ، توجه داشته باشید." »
یک مثال میزنیم فرض کنید IUserRepository را به عنوان یک سرویس Scoped ثبت کرده ایم ، و دو سرویس IUserAccountManager و IUserFinancialManager به این سرویس وابسته هستند و این سرویس درون سازندهی دو سرویس Manager ثبت شده هست . حالا این دو سرویس خودشان درون UserController ثبت شده اند .
public class UserAccountManager : IUserAccountManager { private I,serRepository _userRepository ; // تزریق درون سازنده } public class UserFinancialManager : IUserFinancialManager { private IUserRepository _userRepository ; // تزریق درون سازنده } public class UserController { IUserFinancialManager _userFinancialManager ; IUserAccountManager _userAccountManager; // تزریق درون سازنده }
در این حالت ، DI Container به ازای هر درخواست به این کنترلر ، یک نمونه از userRepository میسازد و این نمونه را در اختیار UserAccountManager و UserFinancialManager میگذارد و در طول درخواست ، هر تغییر وضعیتی درون userRepository بین دو سرویس Manager ، مشترک هست ... این معنای « "سرویسهای Scoped در محدودهی درخواست، مانند Singleton عمل میکنند و شیء ساخته شده و وضعیت آن در بین تمامی سرویسهایی که به آن نیاز دارند، مشترک است. » میباشد ... به عبارت سادهتر ،در این مثال هر دو سرویس Manager به یک نمونه از DbContext درون UserRepository دسترسی دارند .
حالا فرض کنید در اینجا IUserRepository را به صورت Transient ثبت کرده باشیم ، در این حالت به ازای هر درخواست به کنترلر مورد بحث ، DI Container برای هر سرویس Manager ، یک نمونهی اختصاصی از IUserRepository میسازد و در اختیار آنها قرار میدهد و هر سرویس یک نمونهی منحصر به فرد از IUserRepository دارد . به عبارت سادهتر ، در این مثال هر سرویس Manager به یک نمونهی اختصاصی از DbContet دسترسی دارد .
برای تست این موضوع میتوانید از تکه کد زیر استفاده کنید :
using System; namespace AspNetCoreDependencyInjection.Services { public interface IUserRepository { public string GID { get; } } public class UserRepository : IUserRepository { public string GID { get; private set; } public UserRepository() { GID = Guid.NewGuid().GetHashCode().ToString("x"); } } //--------------------------------------------- public interface IUserAccountManager { public string DisplayGuid(); } public class UserAccountManager : IUserAccountManager { private IUserRepository _userRepository; public UserAccountManager(IUserRepository userRepository) { _userRepository = userRepository; } public string DisplayGuid() { return "UserFinancialManager Guid : " + _userRepository.GID; } } //------------------------------------------------- public interface IUserFinancialManager { public string DisplayGuid(); } public class UserFinancialManager : IUserFinancialManager { private IUserRepository _userRepository; public UserFinancialManager(IUserRepository userRepository) { _userRepository = userRepository; } public string DisplayGuid() { return "UserFinancialManager Guid : " + _userRepository.GID; } } }
حالا در کنترلر
public class UserController : Controller { private readonly IUserFinancialManager _userFinancialManager; private readonly IUserAccountManager _userAccountManager; public UserController(IUserFinancialManager userFinancialManager, IUserAccountManager userAccountManager) { _userFinancialManager = userFinancialManager; _userAccountManager = userAccountManager; } public IActionResult Index() { ViewBag.FinancialManager = _userFinancialManager.DisplayGuid(); ViewBag.AccountManager = _userAccountManager.DisplayGuid(); return View(); } }
و نمای ایندکس
@{ ViewData["Title"] = "User"; } <div class="text-center"> <div class="row"> <div class="col-12"> <p class="alert alert-info text-left"> <b>User Finanacial Manager / UserRepository Guid : </b><span>@ViewBag.FinancialManager </span> <br /> <b>User Account Manager / UserRepository Guid : </b><span>@ViewBag.AccountManager</span> <br /> </p> </div> </div> </div>
برای تست یکبار سرویس IUserRepository را به صورت Scoped و بار دیگر به صورت Transient ثبت کنید و با اجرا برنامه Guidهای ایجاد شده را چک کنید .
services.AddTransient<IUserRepository, UserRepository>(); services.AddScoped<IUserAccountManager, UserAccountManager>(); services.AddScoped<IUserFinancialManager, UserFinancialManager>(); // اجرای دوم // services.AddScoped<IUserRepository, UserRepository>(); //services.AddScoped<IUserAccountManager, UserAccountManager>(); //services.AddScoped<IUserFinancialManager, UserFinancialManager>();