ارتقاء به ASP.NET Core 1.0 - قسمت 6 - سرویس‌ها و تزریق وابستگی‌ها
اندازه‌ی قلم متن
تخمین مدت زمان مطالعه‌ی مطلب: ده دقیقه

پیشنیازها (الزامی)

«بررسی مفاهیم معکوس سازی وابستگی‌ها و ابزارهای مرتبط با آن»
«اصول طراحی SOLID»
«مطالعه‌ی بیشتر»


تزریق وابستگی‌ها (یا Dependency injection = DI) به معنای ارسال نمونه‌ای/وهله‌ای از وابستگی (یک سرویس) به شیء وابسته‌ی به آن (یک کلاینت) است. در فرآیند تزریق وابستگی‌ها، یک کلاس، وهله‌های کلاس‌های دیگر مورد نیاز خودش را بجای وهله سازی مستقیم، از یک تزریق کننده دریافت می‌کند. بنابراین بجای نوشتن newها در کلاس جاری، آن‌ها را به صورت وابستگی‌هایی در سازنده‌ی کلاس تعریف می‌کنیم تا توسط یک IoC Container تامین شوند. در اینجا به فریم ورک‌هایی که کار وهله سازی این وابستگی‌ها را انجام می‌دهند، IoC Container و یا DI container می‌گوییم (IoC =  inversion of control ).
چندین نوع تزریق وابستگی‌ها وجود دارند که دو حالت زیر، عمومی‌ترین آن‌ها است:
الف) تزریق در سازنده‌ی کلاس: لیست وابستگی‌های یک کلاس، به عنوان پارامترهای سازنده‌ی آن ذکر می‌شوند.
ب) تزریق در خواص یا Setter injection: کلاینت خواصی get و set را به صورت public معرفی می‌کند و سپس IoC Container با وهله سازی آن‌ها، وابستگی‌های مورد نیاز را تامین خواهد کرد.


تزریق وابستگی‌ها در ASP.NET Core

برخلاف نگارش‌های قبلی ASP.NET، این نگارش جدید از ابتدا با دید پشتیبانی کامل از DI طراحی شده‌است و این مفهوم، در سراسر اجزای آن به صورت یکپارچه‌ای پشتیبانی می‌شود. همچنین به همراه یک minimalistic DI container توکار نیز هست .
این IoC Container توکار از 4 حالت طول عمر ذیل پشتیبانی می‌کند:
- instance: در هربار نیاز به یک وابستگی خاص، تنها یک وهله از آن در اختیار مصرف کننده قرار می‌گیرد و در اینجا شما هستید که مسئول تعریف نحوه‌ی وهله سازی این شیء خواهید بود (برای بار اول).
- transient: هربار که نیاز به وابستگی خاصی بود، یک وهله‌ی جدید از آن توسط IoC Container تولید و ارائه می‌شود.
- singleton: در این حالت تنها یک وهله از وابستگی درخواست شده در طول عمر برنامه تامین می‌شود.
- scoped: در طول عمر یک scope خاص، تنها یک وهله از وابستگی درخواست شده، در اختیار مصرف کننده‌ها قرار می‌گیرد. برای مثال مرسوم است که به ازای یک درخواست وب، تنها یک وهله از شیء‌ایی خاص در اختیار تمام مصرف کننده‌های آن قرار گیرد (single instance per web request).

طول عمر singleton، برای سرویس‌ها و کلاس‌های config مناسب هستند. به این ترتیب به کارآیی بالاتری خواهیم رسید و دیگر نیازی نخواهد بود تا هر بار این اطلاعات خوانده شوند. حالت scoped برای وهله سازی الگوی واحد کار و پیاده سازی تراکنش‌ها مناسب است. برای مثال در طی یک درخواست وب، یک تراکنش باید صورت گیرد.
حالت scoped در حقیقت نوع خاصی از حالت transient است. در حالت transient صرفنظر از هر حالتی، هربار که وابستگی ویژه‌ای درخواست شود، یک وهله‌ی جدید از آن تولید خواهد شد. اما در حالت scoped فقط یک وهله‌ی از وابستگی مورد نظر، در بین تمام اشیاء وابسته‌ی به آن، در طول عمر آن scope تولید می‌شود.
بنابراین در برنامه‌های وب دو نوع singleton برای معرفی کلاس‌های config و نوع scoped برای پیاده سازی تراکنش‌ها و همچنین بالابردن کارآیی برنامه در طی یک درخواست وب (با عدم وهله سازی بیش از اندازه‌ی از کلاس‌های مختلف مورد نیاز)، بیشتر از همه به کار برده می‌شوند.


یک مثال کاربردی: بررسی نحوه‌ی تزریق یک سرویس سفارشی به کمک IoC Container توکار ASP.NET Core


مثال جاری که بر اساس ASP.NET Core Web Application و با قالب خالی آن ایجاد شده‌است، دارای نام فرضی Core1RtmEmptyTest است. در همین پروژه بر روی پوشه‌ی src، کلیک راست کرده و گزینه‌ی Add new project را انتخاب کنید و سپس یک پروژه‌ی جدید از نوع NET Core -> Class library. را به آن، با نام Core1RtmEmptyTest.Services اضافه کنید (تصویر فوق).
در ادامه کلاس نمونه‌ی سرویس پیام‌ها را به همراه اینترفیس آن، با محتوای زیر به آن اضافه کنید:
namespace Core1RtmEmptyTest.Services
{
    public interface IMessagesService
    {
        string GetSiteName();
    }
 
    public class MessagesService : IMessagesService
    {
        public string GetSiteName()
        {
            return "DNT";
        }
    }
}
در ادامه به پروژه‌ی Core1RtmEmptyTest مراجعه کرده و بر روی گره references آن کلیک راست کنید. در اینجا گزینه‌ی add reference را انتخاب کرده و سپس Core1RtmEmptyTest.Services را انتخاب کنید، تا اسمبلی آن‌را بتوان در پروژه‌ی جاری استفاده کرد.


انجام اینکار معادل است با افزودن یک سطر ذیل به فایل project.json پروژه:
{
    "dependencies": {
        // same as before
        "Core1RtmEmptyTest.Services": "1.0.0-*"
    },
در ادامه قصد داریم این سرویس را به متد Configure کلاس Startup تزریق کرده و سپس خروجی رشته‌ای آن‌را توسط میان افزار Run آن نمایش دهیم. برای این منظور فایل Startup.cs را گشوده و امضای متد Configure را به نحو ذیل تغییر دهید:
public void Configure(
    IApplicationBuilder app,
    IHostingEnvironment env,
    IMessagesService messagesService)
همانطور که در قسمت قبل نیز عنوان شد، متد Configure دارای امضای ثابتی نیست و هر تعداد سرویسی را که نیاز است، می‌توان در اینجا اضافه کرد. یک سری از سرویس‌ها مانند IApplicationBuilder و IHostingEnvironment پیشتر توسط IoC Container توکار ASP.NET Core معرفی و ثبت شده‌اند. به همین جهت، همینقدر که در اینجا ذکر شوند، کار می‌کنند و نیازی به تنظیمات اضافه‌تری ندارند. اما سرویس IMessagesService ما هنوز به این IoC Container معرفی نشده‌است. بنابراین نمی‌داند که چگونه باید این اینترفیس را وهله سازی کند.
public void Configure(
    IApplicationBuilder app,
    IHostingEnvironment env,
    IMessagesService messagesService)
{ 
    app.Run(async context =>
    {
        var siteName = messagesService.GetSiteName();
        await context.Response.WriteAsync($"Hello {siteName}");
    });
}
در این حالت اگر برنامه را اجرا کنیم، به این خطا برخواهیم خورد:
 System.InvalidOperationException
No service for type 'Core1RtmEmptyTest.Services.IMessagesService' has been registered.
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
at Microsoft.AspNetCore.Hosting.Internal.ConfigureBuilder.Invoke(object instance, IApplicationBuilder builder)

System.Exception
Could not resolve a service of type 'Core1RtmEmptyTest.Services.IMessagesService' for the parameter 'messagesService' of method 'Configure' on type 'Core1RtmEmptyTest.Startup'.
at Microsoft.AspNetCore.Hosting.Internal.ConfigureBuilder.Invoke(object instance, IApplicationBuilder builder)
at Microsoft.AspNetCore.Hosting.Internal.WebHost.BuildApplication()
برای رفع این مشکل، به متد ConfigureServices کلاس Startup مراجعه کرده و سیم کشی‌های مرتبط را انجام می‌دهیم. در اینجا باید اعلام کنیم که «هر زمانیکه به IMessagesService رسیدی، یک وهله‌ی جدید (transient) از کلاس MessagesService را به صورت خودکار تولید کن و سپس در اختیار مصرف کننده قرار بده»:
public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddTransient<IMessagesService, MessagesService>();
    }
در اینجا نحوه‌ی ثبت یک سرویس را در IoC Containser توکار ASP.NET Core ملاحظه می‌کنید. تمام حالت‌های طول عمری که در ابتدای بحث عنوان شدند، یک متد ویژه‌ی خاص خود را در اینجا دارند. برای مثال حالت transient دارای متد ویژه‌ی AddTransient است و همینطور برای سایر حالت‌ها. این متدها به صورت جنریک تعریف شده‌اند و آرگومان اول آن‌ها، اینترفیس سرویس و آرگومان دوم، پیاده سازی آن‌ها است (سیم کشی اینترفیس، به کلاس پیاده سازی کننده‌ی آن).

پس از اینکار، مجددا برنامه را اجرا کنید. اکنون این خروجی باید مشاهده شود:


و به این معنا است که اکنون IoC Cotanier توکار ASP.NET Core، می‌داند زمانیکه به IMessagesService رسید، چگونه باید آن‌را وهله سازی کند.


چه سرویس‌هایی به صورت پیش فرض در IoC Container توکار ASP.NET Core ثبت شده‌اند؟

در ابتدای متد ConfigureServices یک break point را قرار داده و برنامه را در حالت دیباگ اجرا کنید:


همانطور که ملاحظه می‌کنید، به صورت پیش فرض 16 سرویس در اینجا ثبت شده‌اند که تاکنون با دو مورد از آن‌ها کار کرده‌ایم.


امکان تزریق وابستگی‌ها در همه جا!

در مثال فوق، سرویس سفارشی خود را در متد Configure کلاس آغازین برنامه تزریق کردیم. نکته‌ی مهم اینجا است که برخلاف نگارش‌های قبلی ASP.NET MVC (یعنی بدون نیاز به تنظیمات خاصی برای قسمت‌های مختلف برنامه)، می‌توان این تزریق‌ها را در کنترلرها، در میان افزارها، در فیلترها در ... همه جا و تمام اجزای ASP.NET Core 1.0 انجام داد و دیگر اینبار نیازی نیست تا نکته‌ی ویژه‌ی نحوه‌ی تزریق وابستگی‌ها در فیلترها یا کنترلرهای ASP.NET MVC را یافته و سپس اعمال کنید. تمام این‌ها از روز اول کار می‌کنند. همینقدر که کار ثبت سرویس خود را در متد ConfigureServices انجام دادید، این سرویس در سراسر اکوسیستم ASP.NET Core، قابل دسترسی است.


نیاز به تعویض IoC Container توکار ASP.NET Core

قابلیت تزریق وابستگی‌های توکار ASP.NET Core صرفا جهت برآورده کردن نیازمندی‌های اصلی آن طراحی شده‌است و نه بیشتر. بنابراین توسط آن قابلیت‌های پیشرفته‌ای را که سایر IoC Containers ارائه می‌دهند، نخواهید یافت. برای مثال تعویض امکانات تزریق وابستگی‌های توکار ASP.NET Core با StructureMap این مزایا را به همراه خواهد داشت:
 • امکان ایجاد child/nested containers (پشتیبانی از سناریوهای چند مستاجری)
 • پشتیبانی از Setter Injection
 • امکان انتخاب سازنده‌ای خاص (اگر چندین سازنده تعریف شده باشند)
 • سیم کشی خودکار یا Conventional "Auto" Registration (برای مثال اتصال اینترفیس IName به کلاس Name به صورت خودکار و کاهش تعداد تعاریف ابتدای برنامه)
 • پشتیبانی توکار از Lazy و Func
 • امکان وهله سازی از نوع‌های concrete (یا همان کلاس‌های معمولی)
 • پشتیبانی از مفاهیمی مانند Interception و AOP
 • امکان اسکن اسمبلی‌های مختلف جهت یافتن اینترفیس‌ها و اتصال خودکار آن‌ها (طراحی‌های افزونه پذیر)


روش تعویض IoC Container توکار ASP.NET Core با StructureMap

جزئیات این جایگزین کردن را در مطلب «جایگزین کردن StructureMap با سیستم توکار تزریق وابستگی‌ها در ASP.NET Core 1.0» می‌توانید مطالعه کنید.
یا می‌توانید از روش فوق استفاده کنید و یا اکنون قسمتی از پروژه‌ی رسمی استراکچرمپ در آدرس https://github.com/structuremap/structuremap.dnx جهت کار با NET Core. طراحی شده‌است. برای کار با آن نیاز است این مراحل طی شوند:
الف) دریافت بسته‌ی نیوگت StructureMap.Dnx
برای این منظور بر روی گره references کلیک راست کرده و گزینه‌ی manage nuget packages را انتخاب کنید. سپس در برگه‌ی browse آن، StructureMap.Dnx را جستجو کرده و نصب نمائید (تیک مربوط به انتخاب pre releases هم باید انتخاب شده باشد):


انجام این مراحل معادل هستند با افزودن یک سطر ذیل به فایل project.json برنامه:
{
    "dependencies": {
        // same as before  
        "StructureMap.Dnx": "0.5.1-rc2-final"
    },
ب) جایگزین کردن Container استراکچرمپ با Container توکار ASP.NET Core
پس از نصب بسته‌ی StructureMap.Dnx، به کلاس آغازین برنامه مراجعه کرده و این تغییرات را اعمال کنید:
public class Startup
{
    public IServiceProvider ConfigureServices(IServiceCollection services)
    {
        services.AddDirectoryBrowser();
 
        var container = new Container();
        container.Configure(config =>
        {
            config.Scan(_ =>
            {
                _.AssemblyContainingType<IMessagesService>();
                _.WithDefaultConventions();
            });
            //config.For<IMessagesService>().Use<MessagesService>();
 
            config.Populate(services);
        });
        container.Populate(services);
 
        return container.GetInstance<IServiceProvider>();
    }
در اینجا ابتدا خروجی متد ConfigureServices، به IServiceProvider تغییر کرده‌است تا استراکچرمپ این تامین کننده‌ی سرویس‌ها را ارائه دهد. سپس Container مربوط به استراکچرمپ، وهله سازی شده و همانند روال متداول آن، یک سرویس و کلاس پیاده سازی کننده‌ی آن معرفی شده‌اند (و یا هر تنظیم دیگری را که لازم بود باید در اینجا اضافه کنید). در پایان کار متد Configure آن و پس از این متد، نیاز است متدهای Populate فراخوانی شوند (اولی تعاریف را اضافه می‌کند و دومی کار تنظیمات را نهایی خواهد کرد).
سپس وهله‌ای از IServiceProvider، توسط استراکچرمپ تامین شده و بازگشت داده می‌شود تا بجای IoC Container توکار ASP.NET Core استفاده شود.
در این مثال چون در متد Scan، کار بررسی اسمبلی لایه سرویس برنامه با قراردادهای پیش فرض استراکچرمپ انجام شده‌است، دیگر نیازی به سطر تعریف config.For نیست. در اینجا هرگاه IName ایی یافت شد، به کلاس Name متصل می‌شود (name هر نامی می‌تواند باشد).
  • #
    ‫۸ سال و ۲ ماه قبل، دوشنبه ۲۱ تیر ۱۳۹۵، ساعت ۱۳:۵۷
    با افزودن بسته structuremapp.dnx خطای زیر رخ داده است:
    Package structuremap 4.2.0.402 is not compatible with netcoreapp1.0 
    • #
      ‫۸ سال و ۲ ماه قبل، دوشنبه ۲۱ تیر ۱۳۹۵، ساعت ۱۴:۲۹
      محتویات فایل project.json خود را ارسال کنید.
        • #
          ‫۸ سال و ۲ ماه قبل، دوشنبه ۲۱ تیر ۱۳۹۵، ساعت ۱۵:۱۸
          - در قسمت فریم ورک‌ها، مشخص نکرده‌اید که اگر بسته‌ای از نگارش RC استفاده کرد، چگونه باید وابستگی‌ها را مدیریت کند:
            "frameworks": {
              "netcoreapp1.0": {}
            },
          - قسمت «انتخاب فریم ورک‌های مختلف در فایل project.json» را مطالعه کنید: « ... بر این مبنا، dotnet5.6 ذکر شده‌ی در قسمت تنظیمات نگارش RTM، به این معنا است که قادر به استفاده‌ی از بسته‌های نیوگت و کتابخانه‌های تولید شده‌ی با نگارش‌های RC نیز خواهید بود (هرچند برنامه از netcoreapp1.0 استفاده می‌کند) ...»
            "frameworks": {
              "netcoreapp1.0": {
                  "imports": [
                      "dotnet5.6",
                      "portable-net45+win8"
                  ]
              }
            },
          این سری بر مبنای قالب خالی استاندارد نگارش RTM تهیه شده‌است (و به مرور تکمیل می‌شود؛ اگر علاقمند به مقایسه‌ی تنظیمات بودید).
  • #
    ‫۸ سال و ۲ ماه قبل، سه‌شنبه ۲۹ تیر ۱۳۹۵، ساعت ۰۵:۲۵
    با سلام. نحوه پیاده سازی SmObjectFactory توسط StructureMap.Dnx به چه صورت می‌باشد؟
    • #
      ‫۸ سال و ۲ ماه قبل، سه‌شنبه ۲۹ تیر ۱۳۹۵، ساعت ۰۵:۳۶
      همان سطرهای 7 تا 19 آخرین قطعه کد ارسالی است.
  • #
    ‫۸ سال و ۱ ماه قبل، پنجشنبه ۱۴ مرداد ۱۳۹۵، ساعت ۰۵:۲۲
    یک نکته‌ی تکمیلی
    یک populate اضافی در اینجا باید حذف شود:
    private IServiceProvider IocConfig(IServiceCollection services)
            {
                var container = new Container();
                container.Configure(config =>
                {
                    //config.Populate(services); ---> اضافی است
                });
                container.Populate(services);
                return container.GetInstance<IServiceProvider>();
            }
  • #
    ‫۸ سال و ۱ ماه قبل، یکشنبه ۱۷ مرداد ۱۳۹۵، ساعت ۱۶:۴۷
    نکته‌ای مهم در مورد تزریق وابستگی‌ها در کلاس ویژه‌ی آغازین برنامه

    عموما کلاس‌های کار با بانک اطلاعاتی، با طول عمر Scoped مشخص می‌شوند (جهت پیاده سازی الگوی واحد کار):
     services.AddScoped<IOperationScoped, Operation>();
    به این معنا که یک وهله از کلاس Operation در طول درخواست جاری در بین تمامی درخواست کننده‌های آن به اشتراک گذاشته خواهد شد. این به اشتراک گذاری فقط زمانی معنا خواهد داشت که طول عمر درخواست جاری را مدنظر داشته باشیم و همچنین تزریق وابستگی IOperationScoped به صورت متداولی در کنترلرها، View Components، فیلترها و غیره انجام شود.
    اما اگر از سرویس فرضی IOperationScoped در متدهای مختلف کلاس آغازین برنامه استفاده کنیم، طول عمری را که دریافت خواهیم کرد singleton خواهد بود و نه Scoped. علت اینجا است که در پشت صحنه، در ابتدای هر درخواست توسط سرویسی به نام IServiceScopeFactory کار وهله سازی scope صورت گرفته و در پایان درخواست این scope رهاسازی می‌شود:
    using (var scope = scopeFactory.CreateScope())
    {
       // ...
    }
    اما در کلاس آغازین برنامه، ما هنوز داخل scope قرار نگرفته‌ایم. بنابراین هر درخواست وهله‌ای از سرویس IOperationScoped با طول عمر Scoped، تنها همان وهله‌ی ابتدایی آن‌را باز می‌گرداند و singleton رفتار می‌کند؛ چون scope ایی ایجاد و تخریب نشده‌است.
    در یک چنین مواردی، برای اطمینان حاصل کردن از dispose شدن سرویس در پایان کار، نیاز است مراحل ایجاد scope و dispose آن‌را به صورت دستی به نحو ذیل مدیریت کنیم:
    public void Configure(IApplicationBuilder app,
       ILoggerFactory loggerFactory,
       IServiceScopeFactory scopeFactory)
    {
       using (var scope = scopeFactory.CreateScope())
       {
         var initializer = scope.ServiceProvider.GetService<IOperationScoped>();
         initializer.SeedAsync().Wait();
       }
    }
    بنابراین به صورت خلاصه
    اگر سرویسی با طول عمر Scoped تعریف شد (و نه سایر حالت‌ها)، درخواست وهله‌ای از آن در کلاس آغازین برنامه، طول عمر singleton را خواهد داشت؛ مگر اینکه صراحتا scope را به نحو فوق ایجاد و تخریب کنید.
    این نکته در متدهای الحاقی که قرار است در کلاس آغازین برنامه اجرا شوند، مفید خواهد بود.
  • #
    ‫۷ سال و ۱۱ ماه قبل، یکشنبه ۱۱ مهر ۱۳۹۵، ساعت ۱۵:۲۷
    یک نکته: دسترسی به سرویس‌ها، زمانیکه امکان استفاده‌ی از HttpContext وجود دارد (روش Service locator):
    using Microsoft.Extensions.DependencyInjection;
    //...
    var studentOperations = Request.HttpContext.RequestServices.GetService<IStudentOperations>();
  • #
    ‫۷ سال و ۸ ماه قبل، پنجشنبه ۱۶ دی ۱۳۹۵، ساعت ۲۲:۴۲
    روش دسترسی به تنظیمات برنامه (و یا هر سرویس دیگری) در متد ConfigureServices

    فرض کنید تنظیمات برنامه چنین شکلی را دارند:
    {
      "IdentityOptions": {
        "Lockout": {
          "MaxFailedAccessAttempts": 10,
          "DefaultLockoutTimeSpan": "0.00:05:00.0000"
        }
      }
    }
    و کلاس‌های معادل آن به صورت ذیل هستند:
    public class SiteSettings
    {
        public Identityoptions IdentityOptions { get; set; }
    }
    
    public class Identityoptions
    {
        public LockoutOptions Lockout { get; set; }
    }
    برای دسترسی به این تنظیمات در متد ConfigureServices، مهم‌ترین نکته، فراخوانی متد services.BuildServiceProvider است:
    public void ConfigureServices(IServiceCollection services)
    {
       services.AddSingleton<IConfigurationRoot>(provider => { return Configuration; });
       services.Configure<SiteSettings>(options => Configuration.Bind(options));
    
       var provider = services.BuildServiceProvider();
       var siteSettingsOptions = provider.GetService<IOptions<SiteSettings>>();
       // now use siteSettingsOptions.Value
    علت اینجا است که در متد ConfigureServices هنوز کار BuildServiceProvider انجام نشده و بدون آن، امکان دسترسی به مقادیر سرویس‌های دیگر برنامه میسر نیست.
    • #
      ‫۵ سال قبل، پنجشنبه ۲۱ شهریور ۱۳۹۸، ساعت ۰۶:۴۶
      اگر بخوام سرویس IExampleService ی که در متد ConfigureService اضافه کردم رو در متد StartUp.Configure فراخوانی کنم، باید چه کرد؟ لازم به ذکره که متد‌های من در این سرویس async هستند
      • #
        ‫۵ سال قبل، پنجشنبه ۲۱ شهریور ۱۳۹۸، ساعت ۱۱:۵۴
        دقیقا همان مثال IMessagesService است که در ابتدای متن بررسی شده:
        public void Configure(
            IApplicationBuilder app,
            IHostingEnvironment env,
            IMessagesService messagesService)
        متدهای async را هم در StartUp.Configure به صورت ()task.GetAwaiter().GetResult باید فراخوانی کرد.
        • #
          ‫۵ سال قبل، پنجشنبه ۲۱ شهریور ۱۳۹۸، ساعت ۱۵:۵۴
          public async Task<HttpClient> GetHttpClientAsync()
                  {
                      var currentContext = _httpContextAccessor.HttpContext;
                      // ...
                  }
          public void ConfigureServices(IServiceCollection services)
          {
                      services.AddHttpContextAccessor();
          در متد configure در startup سرویس خودم رو به این شکل فراخوانی کردم
          var df = languageService.GetAll().GetAwaiter().GetResult();
          اما مقدار currentContext در متد GetHttpClient نال هست. آیا اشتباهی در تزریق وابستگی‌ها رخ داده؟ 
          • #
            ‫۵ سال قبل، پنجشنبه ۲۱ شهریور ۱۳۹۸، ساعت ۱۶:۰۷
            خیر. زمان اجرای کدهای program.cs (نقطه‌ی آغازین برنامه‌ی وب)، متد StartUp.Configure خارج از context یک درخواست وب اجرا می‌شود (پیش از اینکه درخواستی پردازش شود). اما میان‌افزارهای ثبت شده‌ی در StartUp.Configure دسترسی به context دارند. اطلاعات بیشتر
  • #
    ‫۷ سال و ۶ ماه قبل، یکشنبه ۶ فروردین ۱۳۹۶، ساعت ۲۳:۴۶
    به روز رسانی
    با حذف فایل project.json در VS 2017، اکنون با کلیک راست بر روی گروه نام پروژه (فایل csproj)، گزینه‌ی Edit آن ظاهر شده و مداخل ذکر شده‌ی در مطلب فوق، چنین تعاریفی را پیدا می‌کنند:  
    <Project Sdk="Microsoft.NET.Sdk.Web">
      <ItemGroup>
        <ProjectReference Include="..\Core1RtmEmptyTest.Services\Core1RtmEmptyTest.Services.csproj" />
      </ItemGroup>
    
      <ItemGroup>
        <PackageReference Include="StructureMap.Dnx" Version="1.2.0" />
      </ItemGroup>
    </Project>
  • #
    ‫۷ سال و ۵ ماه قبل، یکشنبه ۲۰ فروردین ۱۳۹۶، ساعت ۰۶:۰۳
    معادل مطلب «Lazy loading در تزریق وابستگی‌ها به کمک StructureMap» در ASP.NET Core
    public void ConfigureServices(IServiceCollection services) 
    {     
       // ...
       services.AddScoped<IEmailService, EmailService>()
                    .AddScoped(x => new Lazy<IEmailService>(() => x.GetRequiredService<IEmailService>())); 
       // ...
    }
    ابتدا سرویس مدنظر به صورت معمولی ثبت می‌شود و سپس نمونه‌ی Lazy آن بر اساس این سرویس تعریف خواهد شد.
    اکنون امکان تعریف و تزریق این سرویس به صورت Lazy در سازنده‌ی کنترلرها وجود خواهد داشت:
    public class EmailController : Controller 
    {   
       private readonly Lazy<IEmailService> _emailService; 
       public EmailController(Lazy<IEmailService> emailService)   
       {        
           _emailService = emailService;
       }
  • #
    ‫۷ سال و ۲ ماه قبل، دوشنبه ۱۲ تیر ۱۳۹۶، ساعت ۱۹:۳۵
    آیا امکان تزریق وابستگی در کلاس پروفایل‌های AutoMapper هم وجود دارد؟
    مثلا دسترسی به IHttpContextAccessor.  
  • #
    ‫۷ سال و ۲ ماه قبل، دوشنبه ۱۲ تیر ۱۳۹۶، ساعت ۲۰:۱۰
    آیا امکان اسکن اسمبلی‌ها با قرارداد پیش فرض در IoC Container توکار ASP.NET Core مثل اسکن StructureMap  وجود داره؟
    • #
      ‫۷ سال و ۲ ماه قبل، دوشنبه ۱۲ تیر ۱۳۹۶، ساعت ۲۲:۵۵
      هستند یکسری پروژه‌ی افزونه پذیر برای ASP.NET Core که این مفاهیم را پیاده سازی کرده‌اند (و وابستگی به StructureMap هم ندارند):
      ExtCore - Free, open source and cross-platform framework for creating modular and extendable web applications based on ASP.NET Core
      SimplCommerce - A super simple, cross platform, modularized ecommerce system built on .NET Core
      Modular Web Application with ASP.NET Core
      Orchard vNext - Orchard 2 is a re-implementation of Orchard CMS in ASP.NET Core
    • #
      ‫۶ سال و ۵ ماه قبل، سه‌شنبه ۲۸ فروردین ۱۳۹۷، ساعت ۲۰:۳۳
      پکیج  Scrutor   جهت  اسکن اسمبلی‌ها با قرارداد پیش فرض ( مستندات )  

       services.Scan(scan =>
                      scan.FromAssemblyOf<UserRepository>()
                          .AddClasses(classes => classes.InNamespaceOf<UserRepository>())
                          .AsSelf()
                          .WithScopedLifetime());

      • #
        ‫۶ سال قبل، یکشنبه ۱۸ شهریور ۱۳۹۷، ساعت ۲۳:۴۴
        سلام
        من می‌خوستم از Scrutor   استفاده کنم 
        کد سرویس من به صورت زیر است
        namespace FirstProjectServices
        {
            public interface IMessagesService
            {
                string GetSiteName();
            }
        
            public class MessagesService : IMessagesService
            {
                public string GetSiteName()
                {
                    return "DNT";
                }
            }
        }
        
        namespace FirstProjectServices
        {
            public interface IMessagesService2
            {
                string GetSiteName();
            }
        
            public class MessagesService2 : IMessagesService2
            {
                public string GetSiteName()
                {
                    return "DNT";
                }
            }
        }
        و در کلاس Startup کد زیر رو نوشتم
        public class Startup
            {
               public void ConfigureServices(IServiceCollection services)
                {
                    var collection = new ServiceCollection();
        
                    services.Scan(scan => scan
                        // We start out with all types in the assembly of ITransientService
                        .FromAssemblyOf<IMessagesService>()
                        .AddClasses(classes => classes.AssignableTo<MessagesService>())
                        .AsImplementedInterfaces()
                        .WithTransientLifetime()
                       );
        
                 }
        
               
                public void Configure(IApplicationBuilder app, IHostingEnvironment env, IMessagesService2 _messagesService)
                {
                    if (env.IsDevelopment())
                    {
                        app.UseDeveloperExceptionPage();
                    }
        
                    // app.UseDefaultFiles();
                    // app.UseStaticFiles();
        
                    app.Run(async (context) =>
                    {
                        string siteName = _messagesService.GetSiteName();
                        await context.Response.WriteAsync($"Site Name {siteName}");
                    });
                }
            }
        در متد ConfigureServices نحوه Register رو مشخص کردم (مپ کردن IMessagesService  به  MessagesService ) و در متد Configure انتظار دارم چون کلاس MessagesService  و MessagesService2 کنار هم هستند این نگاشت به درستی انجام شود و خطا میده که   نگاشت I MessagesService  تعریف نشده است. در صورتی که هدف ما اسکن کل اسمبلی هست. ممنون میشم راهنمایی کنید.
        • #
          ‫۶ سال قبل، دوشنبه ۱۹ شهریور ۱۳۹۷، ساعت ۰۵:۳۲
          - تنظیمات شما اسمبلی حاوی IMessagesService را اسکن می‌کند و تمام کلاس‌هایی را که از کلاس MessagesService مشتق شده باشند، به لیست سرویس‌های ثبت شده‌ی سیستم تزریق وابستگی‌ها اضافه خواهد کرد. یعنی هیچ کلاسی را.
          - شما در عمل به دنبال اسکن کل اسمبلی و ثبت و اتصال IClassName‌ها به ClassName‌ها با طول عمر Transient هستید (چیزی شبیه به عملیات اسکن پیش‌فرض StructureMap) که در اینجا چنین شکلی را پیدا می‌کند:
          public class Startup
          {
              public void ConfigureServices(IServiceCollection services)
              {
                  services.Scan(scan => scan.FromAssemblyOf<IMessagesService>()
                                            .AddClasses()
                                            .AsMatchingInterface() // Registers all <`IClassName`, `ClassName`>
                                            .WithTransientLifetime()
                         );
  • #
    ‫۷ سال و ۱ ماه قبل، پنجشنبه ۲۶ مرداد ۱۳۹۶، ساعت ۱۷:۴۶
    یک نکته‌ی تکمیلی: تغییرات سرویس‌های پیش فرض ثبت شده‌ی در ASP.NET Core 2.0

    در ASP.NET Core 2.0 تعداد سرویس‌های پیش فرض ثبت شده‌ی در سیستم 33 مورد است:


    که لیست آن‌ها به شرح ذیل است:

    Lifetime = Singleton, ServiceType = {Microsoft.AspNetCore.Hosting.IHostingEnvironment}, ImplementationType = null
    Lifetime = Singleton, ServiceType = {Microsoft.AspNetCore.Hosting.WebHostBuilderContext}, ImplementationType = null
    Lifetime = Singleton, ServiceType = {Microsoft.Extensions.Configuration.IConfiguration}, ImplementationType = null
    Lifetime = Transient, ServiceType = {Microsoft.AspNetCore.Hosting.Builder.IApplicationBuilderFactory}, ImplementationType = {Microsoft.AspNetCore.Hosting.Builder.ApplicationBuilderFactory}
    Lifetime = Transient, ServiceType = {Microsoft.AspNetCore.Http.IHttpContextFactory}, ImplementationType = {Microsoft.AspNetCore.Http.HttpContextFactory}
    Lifetime = Scoped, ServiceType = {Microsoft.AspNetCore.Http.IMiddlewareFactory}, ImplementationType = {Microsoft.AspNetCore.Http.MiddlewareFactory}
    Lifetime = Singleton, ServiceType = {Microsoft.Extensions.Options.IOptions`1[TOptions]}, ImplementationType = {Microsoft.Extensions.Options.OptionsManager`1[TOptions]}
    Lifetime = Scoped, ServiceType = {Microsoft.Extensions.Options.IOptionsSnapshot`1[TOptions]}, ImplementationType = {Microsoft.Extensions.Options.OptionsManager`1[TOptions]}
    Lifetime = Singleton, ServiceType = {Microsoft.Extensions.Options.IOptionsMonitor`1[TOptions]}, ImplementationType = {Microsoft.Extensions.Options.OptionsMonitor`1[TOptions]}
    Lifetime = Transient, ServiceType = {Microsoft.Extensions.Options.IOptionsFactory`1[TOptions]}, ImplementationType = {Microsoft.Extensions.Options.OptionsFactory`1[TOptions]}
    Lifetime = Singleton, ServiceType = {Microsoft.Extensions.Options.IOptionsMonitorCache`1[TOptions]}, ImplementationType = {Microsoft.Extensions.Options.OptionsCache`1[TOptions]}
    Lifetime = Singleton, ServiceType = {Microsoft.Extensions.Logging.ILoggerFactory}, ImplementationType = {Microsoft.Extensions.Logging.LoggerFactory}
    Lifetime = Singleton, ServiceType = {Microsoft.Extensions.Logging.ILogger`1[TCategoryName]}, ImplementationType = {Microsoft.Extensions.Logging.Logger`1[T]}
    Lifetime = Singleton, ServiceType = {Microsoft.Extensions.Options.IConfigureOptions`1[Microsoft.Extensions.Logging.LoggerFilterOptions]}, ImplementationType = null
    Lifetime = Transient, ServiceType = {Microsoft.AspNetCore.Hosting.IStartupFilter}, ImplementationType = {Microsoft.AspNetCore.Hosting.Internal.AutoRequestServicesStartupFilter}
    Lifetime = Singleton, ServiceType = {Microsoft.Extensions.ObjectPool.ObjectPoolProvider}, ImplementationType = {Microsoft.Extensions.ObjectPool.DefaultObjectPoolProvider}
    Lifetime = Singleton, ServiceType = {Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal.ITransportFactory}, ImplementationType = {Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.LibuvTransportFactory}
    Lifetime = Transient, ServiceType = {Microsoft.Extensions.Options.IConfigureOptions`1[Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerOptions]}, ImplementationType = {Microsoft.AspNetCore.Server.Kestrel.Core.Internal.KestrelServerOptionsSetup}
    Lifetime = Singleton, ServiceType = {Microsoft.AspNetCore.Hosting.Server.IServer}, ImplementationType = {Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServer}
    Lifetime = Singleton, ServiceType = {Microsoft.Extensions.Options.IConfigureOptions`1[Microsoft.Extensions.Logging.LoggerFilterOptions]}, ImplementationType = null
    Lifetime = Singleton, ServiceType = {Microsoft.Extensions.Options.IOptionsChangeTokenSource`1[Microsoft.Extensions.Logging.LoggerFilterOptions]}, ImplementationType = null
    Lifetime = Singleton, ServiceType = {Microsoft.Extensions.Logging.ILoggerProvider}, ImplementationType = {Microsoft.Extensions.Logging.Console.ConsoleLoggerProvider}
    Lifetime = Singleton, ServiceType = {Microsoft.Extensions.Logging.ILoggerProvider}, ImplementationType = {Microsoft.Extensions.Logging.Debug.DebugLoggerProvider}
    Lifetime = Singleton, ServiceType = {Microsoft.Extensions.DependencyInjection.IServiceProviderFactory`1[Microsoft.Extensions.DependencyInjection.IServiceCollection]}, ImplementationType = null
    Lifetime = Singleton, ServiceType = {Microsoft.AspNetCore.Hosting.IStartup}, ImplementationType = null
    Lifetime = Singleton, ServiceType = {Microsoft.Extensions.Options.IConfigureOptions`1[Microsoft.Extensions.Logging.LoggerFilterOptions]}, ImplementationType = null
    Lifetime = Singleton, ServiceType = {Microsoft.Extensions.Options.IOptionsChangeTokenSource`1[Microsoft.Extensions.Logging.LoggerFilterOptions]}, ImplementationType = null
    Lifetime = Singleton, ServiceType = {Microsoft.Extensions.Logging.ILoggerProvider}, ImplementationType = {Microsoft.Extensions.Logging.Console.ConsoleLoggerProvider}
    Lifetime = Singleton, ServiceType = {Microsoft.Extensions.Logging.ILoggerProvider}, ImplementationType = {Microsoft.Extensions.Logging.Debug.DebugLoggerProvider}
    Lifetime = Singleton, ServiceType = {System.Diagnostics.DiagnosticListener}, ImplementationType = null
    Lifetime = Singleton, ServiceType = {System.Diagnostics.DiagnosticSource}, ImplementationType = null
    Lifetime = Singleton, ServiceType = {Microsoft.AspNetCore.Hosting.IApplicationLifetime}, ImplementationType = {Microsoft.AspNetCore.Hosting.Internal.ApplicationLifetime}
    Lifetime = Singleton, ServiceType = {Microsoft.AspNetCore.Hosting.Internal.HostedServiceExecutor}, ImplementationType = {Microsoft.AspNetCore.Hosting.Internal.HostedServiceExecutor}
    در اینجا حتی IConfiguration را به عنوان یک سرویس از پیش تعریف شده ملاحظه می‌کنید (سومین مورد ثبت شده) و این سرویس قابلیت تزریق در سازنده‌ی کلاس Startup را نیز دارا است:
        public class Startup
        {
            public Startup(IConfiguration configuration)
            {
                Configuration = configuration;
            }
    
            public IConfiguration Configuration { get; }
     چون در فایل Program.cs توسط متد جدید WebHost.CreateDefaultBuilder تنظیم و وهله سازی خواهد شد:
            public static IWebHost BuildWebHost(string[] args) =>
                WebHost.CreateDefaultBuilder(args)
                    .UseStartup<Startup>()
                    .ConfigureLogging((hostingContext, logging) =>
                    {
                        logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
                        logging.AddConsole();
                        logging.AddDebug();
                    })
                    .Build();
  • #
    ‫۷ سال و ۱ ماه قبل، شنبه ۲۸ مرداد ۱۳۹۶، ساعت ۱۵:۱۲
    تکمیل نکته‌ی «نکته‌ای مهم در مورد تزریق وابستگی‌ها در کلاس ویژه‌ی آغازین برنامه » که در بالا عنوان شد

    در ASP.NET Core 2.0 در صورت عدم رعایت این نکته و عدم ایجاد یک scope جدید، ممکن است خطای ذیل را دریافت کنید:
    Unhandled Exception: System.InvalidOperationException: Cannot resolve scoped service from root provider.
    تنظیم این بررسی در فایل program.cs صورت می‌گیرد:
    namespace ASPNETCoreIdentitySample
    {
        public class Program
        {
            public static void Main(string[] args)
            {
                var host = new WebHostBuilder()
                   // ...
                    .UseDefaultServiceProvider((context, options) =>
                    {
                        options.ValidateScopes = context.HostingEnvironment.IsDevelopment();
                    })
                   // ...
    
                host.Run();
            }
        }
    }
    در اینجا اگر ValidateScopes را false کنید این استثناء را دریافت نخواهید کرد. اما بهتر است این تنظیم وجود داشته باشد تا مشکلاتی از این دست پیش از ارائه‌ی نهایی برنامه مشخص شوند. راه حل صحیح آن هم ایجاد یک scope جدید توسط scopeFactory.CreateScope است که به آن اشاره شد.
  • #
    ‫۶ سال و ۳ ماه قبل، جمعه ۱ تیر ۱۳۹۷، ساعت ۱۲:۴۱
    کتابخانه‌های کمکی جهت ساده سازی ثبت تنظیمات اولیه‌ی IoC Container توکار NET Core.
    - Scrutor  
    • #
      ‫۶ سال و ۱ ماه قبل، دوشنبه ۸ مرداد ۱۳۹۷، ساعت ۰۲:۰۸
      با سلام؛ در asp.net core 2.1 از چه پکیجی برای ioc استفاده میکنید؟ من سمپل شمارو در این مقاله انجام دادم ولی addmvc از کار افتاد و صفحه سفید و خالی در مرورگر بالا میاد. 
      • #
        ‫۶ سال و ۱ ماه قبل، دوشنبه ۸ مرداد ۱۳۹۷، ساعت ۰۲:۱۳
        مطلقا از IoC Containers ثالث در اینجا استفاده نمی‌کنم. هیچ نیازی نیست و IoC Container خود آن به خوبی برای اکثر کارها پاسخگو است. در ابتدای کار کسانیکه به ASP.NET Core مهاجرت می‌کنند، چون هنوز حال و هوای MVC 5 را دارند، به دنبال IoC Containers ثالث هستند ... اما واقعا در اینجا در اکثر موارد نیازی به آن‌ها ندارید. «کتابخانه‌های کمکی جهت ساده سازی ثبت تنظیمات اولیه‌ی IoC Container توکار NET Core.» هم راه حلی هستند برای پیاده سازی امکانات مشابه، اما بدون استفاده از کتابخانه‌های ثالث IoC.
        • #
          ‫۶ سال و ۱ ماه قبل، دوشنبه ۸ مرداد ۱۳۹۷، ساعت ۱۶:۲۷
          امکانی که در قسمت "نیاز به تعویض IoC Container توکار ASP.NET Core  " در همین مقاله فرمودین رو چه کار کنیم؟ :
           • امکان اسکن اسمبلی‌های مختلف جهت یافتن اینترفیس‌ها و اتصال خودکار آن‌ها (طراحی‌های افزونه پذیر)
  • #
    ‫۶ سال و ۲ ماه قبل، جمعه ۲۹ تیر ۱۳۹۷، ساعت ۰۷:۰۸
    چگونگی معرفی و استفاده از یک اینترفیس در صورتی که چند کلاس  پیاده ساز داشته باشد : 
    اینترفیس IMultiple و دو کلاس پیاده سازی کننده : 
        public interface IMultiple {
            string GetName ();
        }
    
        public class ImplementationOne : IMultiple {
            public string GetName () {
                return "Abolfazl Roshanzamir";
            }
        }
    
        public class ImplementationTwo : IMultiple {
            public string GetName () {
                return "َAndy Madadian";
            }
        }
    ثبت سرویس : 
                services.AddScoped<ImplementationOne> ();
                services.AddScoped<ImplementationTwo> ();
                services.AddScoped<Func<string, IMultiple>> (serviceProvider => key => {
                    switch (key) {
                        case "A":
                            return serviceProvider.GetService<ImplementationOne> ();
                        case "B":
                            return serviceProvider.GetService<ImplementationTwo> ();
                        default:
                            throw new KeyNotFoundException (); // or maybe return null, up to you
                    }
                });

    استفاده از سرویس همراه با مشخص کردن پیاده ساز مورد نظر
    private readonly Func<string, IMultiple> _serviceAccessor;
    
    public HomeController (Func<string, IMultiple> serviceAccessor) {
         this._serviceAccessor = serviceAccessor;
    }
    public IActionResult Index () {
        var implementOne = this._serviceAccessor ("A").GetName (); // Abolfazl Roshanzamir 
        var implementTwo = this._serviceAccessor ("B").GetName (); // Andy Madadian 
        return View ();
    }

     
    • #
      ‫۶ سال و ۲ ماه قبل، شنبه ۳۰ تیر ۱۳۹۷، ساعت ۱۳:۵۷
      یک نکته‌ی تکمیلی:  تزریق وابستگی‌ها در حالتی‌که از یک اینترفیس چندین کلاس مشتق شده‌اند

      نمونه‌ی این نکته را پیشتر با Structure Map ملاحظه کرده بودید. پیاده سازی آن با امکانات توکار تزریق وابستگی‌های NET Core. یا بر اساس روش Factory است که در نکته‌ی قبل ملاحظه می‌کنید و یا اگر از یک اینترفیس چندین پیاده سازی در برنامه وجود داشته باشند و ارتباطات آن‌ها در ابتدای کار برنامه به سیستم توکار تزریق وابستگی‌های NET Core. معرفی شده باشند، فقط کافی است یک <IEnumerable<IMultiple را به سازنده‌ی کلاس سرویس استفاده کننده تزریق کنیم:
      private readonly IEnumerable<IMultiple> _services;
      
      public HomeController (IEnumerable<IMultiple> services) 
      {
           _services = services;
      }
      این IEnumerable لیست تمام وهله‌های از این نوع را در اختیار سرویس جاری قرار می‌دهد. در این حالت دیگر نیازی به استفاده از factoryها نیست.

      مرحله‌ی بعد، تشخیص و یا انتخاب یک پیاده سازی خاص است. الان لیستی از وهله‌های تزریق شده را در اختیار داریم؛ اما می‌خواهیم فقط از یکی از آن‌ها استفاده کنیم:
      الف) انتخاب سرویس مدنظر بر اساس نوع کلاسی خاص
       var serviceA = services.First(o => o.GetType() == typeof(ImplementationOne));
      ب) نامدار کردن وهله‌ی مدنظر
      این روشی است که برای مثال در Structure Map هم استفاده می‌شود (تحت عنوان named instances). یک خاصیت Name را به اینترفیسی که چندین پیاده سازی دارد، اضافه کنید. سپس بر اساس این Name کوئری بگیرید:
       var serviceB = services.First(o => o.Name.Equals("MyClassName"));
  • #
    ‫۵ سال قبل، چهارشنبه ۲۷ شهریور ۱۳۹۸، ساعت ۱۳:۱۵
    یک نکته‌ی تکمیلی: در ASP.NET Core 3.0 فراموش شدن ثبت سرویس‌ها در ابتدای اجرای برنامه گوشزد می‌شود

    فرض کنید WeatherForecastService شما به DataService وابستگی دارد:
    public class WeatherForecastService
    {
        private readonly DataService _dataService;
        public WeatherForecastService(DataService dataService)
        {
            _dataService = dataService;
        }
    و اکنون از این سرویس در یک کنترلر استفاده کرده‌اید:
    public class WeatherForecastController : ControllerBase
    {
        private readonly WeatherForecastService _service;
        public WeatherForecastController(WeatherForecastService service)
        {
            _service = service;
        }
    و در این بین، در حین معرفی وابستگی‌ها:
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers();
        services.AddSingleton<WeatherForecastService>();
    ثبت سرویس Data فراموش شده‌است. اکنون اگر برنامه را اجرا کنید، پیش از شروع به کار، این اعتبارسنجی رخ خواهد داد:
    Unhandled exception. System.AggregateException: Some services are not able to be constructed
    (Error while validating the service descriptor 
        'ServiceType: TestApp.WeatherForecastService Lifetime: Scoped ImplementationType:
         TestApp.WeatherForecastService': Unable to resolve service for type
        'TestApp.DataService' while attempting to activate 'TestApp.WeatherForecastService'.)
    به این ترتیب قبل از شروع برنامه، کمبودهای تنظیمات سیستم تزریق وابستگی‌ها گوشزد می‌شود.
  • #
    ‫۴ سال و ۹ ماه قبل، دوشنبه ۱۸ آذر ۱۳۹۸، ساعت ۱۴:۲۵
    یک سوال؛ خیلی از این متدهایی که کلاس و اینترفیس براش میسازید را بدون تزریق وابستگی هم میشه با نوشتن یک کلاس static و بدون انجام این کار‌ها انجام داد؛ چرا ما از تزریق وابستگی و اینترفیس استفاده می‌کنیم. آیا چون متدهای استاتیک همیشه در حافظه هستند، مشکلی است که دارند؟
      • #
        ‫۴ سال و ۹ ماه قبل، چهارشنبه ۲۰ آذر ۱۳۹۸، ساعت ۱۸:۳۲
        من تمام مباحثی رو که زحمت کشیدن و ارائه کردین همراه با مسائلی که داخل نظرات این مطالب ارائه شده بود را مطالعه و حتی تست کردم که البته یه سری‌اش رو قبلا خونده بودم؛ ولی بازم چند تا سوال برام مطرح شد که تو این مطالب یا نظراتش جوابی دریافت نکردم

        1-چرا ما از namespace و class Library جداگانه استفاده میکنیم؛ در صورتی که میشه با پوشه بندی این کار انجام داد. از این رو میپرسم که ما به جز متدهای الحاقی و یه سری متدهای کمکی و همیشگی مثل ارسال ایمیل و مثلا تولید اعداد تصادفی و کپچا، اکثر متدهایی که استفاده میکنیم مربوط به همین پروژه است و در پروژه‌های دیگه کارایی نداره. پس منطقی هست که کلاس‌ها و متدهایی که توی پروژه‌های دیگه میشه ازشون استفاده کرد رو به صورت یک class Library جداگانه تعریف کرد و بقیه پروژه داخل یک class Library. منطق جداکردن را متوجه نمیشم .
        2- این نوع لایه بندی و تزریق وابستگی برای پروژه‌های کوچیک مثل یک سایت معرفی یک کسب و کار و شاید فروش چند تا محصول هم کارآیی داره یا به خاطر حجم کدنویسی بالا و حتی پیچیدگی در بعضی موارد که این بخش مال کدوم لایه است فقط برای پروژه‌های بزرگ و تصمیم بر توسعه بیشتر استفاده میشه.