شما اینجا هستید: خانه / مقالات آموزشی / پیاده سازی Dependency Injection در ASP.NET Web API

پیاده سازی Dependency Injection در ASP.NET Web API

Dependency Injection (به اختصار DI، ترجمه فارسی : تزریق وابستگی) الگویی است که جهت پیاده سازی اصل Dependency Inversion در طراحی شی گراء مطرح شده است. در صورتی که با مفاهیم Dependency Injection و IoC Container آشنایی ندارید، می توانید به مطالعه ی این مقاله از آقای Martin Fowler بپردازید. Castle Windsor یکی از  IoC Container های معروف، رایگان و متن باز نوشته شده برای NET. می باشد. در این مقاله به پیاده سازی این الگو در ASP.NET Web API با کتابخانه ی Castle Windsor میپردازیم.

 

  • پیاده سازی Controller ها و Service ها

ابتدا Controller ها و کلاس های سرویس مورد نظر را خود را پیاده سازی کنید. سپس با استفاده از Constructor Injection کلاس های سرویس خود را به داخل Controller ها Inject کنید. مثال ساده ی زیر را در نظر بگیرید :

 
 

  • اضافه کردن و Config کتابخانه Castle Windsor به پروژه

جهت اضافه کردن این کتابخانه به پروژه از دستور زیر در Package Manager Console استفاده نمایید :

برای کار با Castle Windsor مانند هر IoCC دیگری، نیاز دارید تا Dependency ها و کلاس های پیاده سازی آنها را معرفی کنید. برای این کار کلاس فوق را در App_Start پروژه ایجاد میکنیم :

 کلاس IUserService و پیاده سازی آن و همچنین کلاس UserController را در Container ثبت کردیم. از آنجا که Controller ها به ازای هر Request باید ایجاد شوند، از متد LifestylePerWebRequest برای ثبت UserController استفاده کردیم. در این مثال فرض شده است که UserService یک سرویس Stateless بوده و تعریف آن به صورت Singleton مانعی ندارد. به همین دلیل هیچ نوع LifeStyle خاصی برای آن نوشته نشده است تا Windsor از LifeStyle پیش فرض (یعنی Singleton) استفاده نماید.

حال کافی است تا ControllerActivator مخصوص به خود را نوشته و در آن از Windsor برای ایجاد Controller ها استفاده نماییم. سپس ControllerActivator نوشته شده را به عنوان سرویس پیش فرض تعریف میکنیم تا WebAPI برای ساخت Controller ها از کلاس فوق استفاده نماید.

  • پیاده سازی ControllerActivator سفارشی

برای ساخت ControllerActivator سفارشی، نیاز به پیاده سازی اینترفیس IHttpControllerActivator دارید :

 همانطور که در کد مشاهده میکنید اینترفیس IWindsorContainer از طریق Constructor به این کلاس تزریق شده است تا Activator نوشته شده بتواند برای ساخت Controller ها از آن استفاده نماید. برای اتمام کار تنها کافی است تا یک نمونه از کلاس CastleControllerActivator را به عنوان ControllerActivator پیش فرض در Global.Asax معرفی کنید :

 

نکته : در صورتی که تعداد Controller های پروژه ی شما زیاد است، می توانید به جای ثبت تک به تک آنها در Container، از امکانات Windsor برای ثبت دسته ای Dependency ها استفاده بکنید. به مثال اصلاح شده ی فوق دقت کنید :

 

rarدانلود سورس برنامه

درباره هادی احمدی

هادی احمدی
برنامه نویس، تحلیلگر و طراح نرم افزار که به فعالیت بر روی بسترهای نرم افزاری مایکروسافت مشغول هستم. علاقه مند به مباحث طراحی و معماری نرم افزار و همچنین پیاده سازی سیستم های اطلاعاتی پیچیده می باشم.

16 نظر

  1. نیما حمیدان

    آقای احمدی سلام
    لطفا آموزش های مربوط به دامین دریون را تکمیل نمایید و بعد سراغ مطالب دیگر روید لطفا از این پراکنده گویی بپرهیزید. ما منتظر بخش های بعدی از این سری هستیم
    متشکرم

    • هادی احمدی

      سلام آقای حمیدان
      بخش سوم از سری آموزش DDD در هفته جاری تکمیل شده و بر روی سایت قرار میگیرد.
      باید در نظر داشته باشید که نوشتن مقالات آموزشی پیرامون بحث طراحی نرم افزار بسیار زمانبر تر از نوشتن در حوزه ی تکنولوژی می باشد. شخصا هم حساسیت خاصی بر روی این مطالب دارم و حتما سعی میکنم در فراغ خاطر کامل شروع به نوشتن این مباحث کنم و پس از نوشتن، چند بار مطالب رو بازبینی میکنم. به همین علت کمی زمان بر است.
      موفق باشید

  2. با سلام و احترام
    با تشکر فراوان از مطلب مفید شما
    می خواستم بدونم چطور به صورت دسته ای کلاس ها و اینترفیس را با هم لینک کنم.
    مثلا نام گذاری ها به صورت زیر باشد
    Product
    IProductService
    ProductServiceImpl
    با تشکر

    • هادی احمدی

      سلام
      خواهش میکنم لطف دارید
      لطفا سوالتون رو با جزئیات بیشتری مطرح بفرمایید، رابطه Product با دو کلاس دیگر رو متوجه نشدم.

  3. سلام
    به عنوان مثال
    اینترفیس های
    IProductService
    IUserService
    ITestService
    توسط کلاس های زیر
    ProductServiceImpl
    UserServiceImpl
    TestServiceImpl
    پیاده سازی شده اند
    حالا برای معرفی به صورت تک تک میخواهیم به صورت دسته ای این کار را انجام دهیم
    مانند مثال خودتون
    //container.Register(Component.For().LifestylePerWebRequest());
    container.Register(Classes.FromThisAssembly().BasedOn().LifestylePerWebRequest());
    حالا به جای عبارت زیر
    container.Register(Component.For().ImplementedBy());
    ؟؟؟؟؟؟
    چی جایگزین کنیم که همه اینتر فیس ها و کلاس های مربوطه به کانتکس معرفی شوند

    • هادی احمدی

      اگر Interface های شما از یک Interface واحد ارث بری میکنند (برای مثال IService) میتونید از این روش استفاده کنید :

      container.Register(Classes.FromThisAssembly().BasedOn< IService >().WithServiceDefaultInterfaces());

  4. آقای احمدی عزیز سلام
    از مطلب با ارزش شما متشکرم
    آیا DI های دیگر را هم بررسی کرده اید؟ مقایسه ای بین DI های متداول دارید؟ مشخصا من بین Castel Windsor / Structure Map / Ninject در اینترنت مقایسه ی خوبی پیدا نکردم. همگی در ویژگی های پایه مثل هم هستند اما در استفاده های advanced که معمولا شش تا دوازده ماه پس از استارت پروژه پیش می آیند خیلی کسی مقایسه ی بدور از تعصب و مناسبی ندارد. شما نظرتان چیست؟

    با احترام و تشکر
    محمد حامد

    • هادی احمدی

      سلام محمد جان
      قبل از پاسخ به سوال شما عرض کنم که، استفاده از عبارت DI برای کتابخانه هایی مثل Windsor و Ninject و … درست نیست. DI در واقع به “تکنیک” پاس کردن از بیرون (یا اصطلاحا تزریق) وابستگی ها به یک کلاس گفته می شود. کتابخانه های مطرح شده در واقع IoC Container هستند. در واقع با کمک Container های فوق می توان این تکنیک را عملیاتی کرد.

      در مورد سوال هم باید بگم که بعضی از Container ها قابلیت های اضافه ای نسبت به بقیه دارند اما به طور کل تفاوت چشم گیری از نظر قابلیت بین آنها وجود ندارد (حداقل بنده ندیدم). یکی از دلایلی که شخصا Castle رو به دو Container دیگری که فرمودید ترجیح میدم، Documentation بهتر و همچنین ابزارهای مکمل قوی تر مثل پروژه های Castle.DynamicProxy و LoggingServices است.
      تقریبا میشه گفت الان همه ی Container های معروف هم سطح هستند و تفاوت و برتری خاصی بینشون وجود نداره.

  5. سلام و عرض ادب خدمت استاد عزیزم

    شاید سوالم بی ربط به موضوع باشه ولی واقعیتش چاره ای نداشتم

    من یه سوال راجع Interceptor های Castle Windsor داشتم

    کل پروژه های من در یک Solution قرار دارند.
    دو تا Bounded Context دارم BC1 و BC2
    هر BC یک لایه Application دارد
    یه کلاس Interceptor در لایه Framework دارم که میخوام روی لایه Application در BC1 و BC2 بزارم

    در کلاس Interceptor یه IUnitOfWork دارم که داخل پارامترهای Constructor گذاشتم.

    برای هر BC در IOC:
    IUnitOfWork مربوط به همان BC رو Register کردم (با Name های جدا )
    کلاس Interceptor مربوط به همان BC رو Register کردم (با Name های جدا) و Depend ش کردم به IUnitOfWork خود اون BC (از طریق نام)

    حالا میخوام هر Application با Interceptor خودش Register بشه ، و یه جورایی وصل بشه به Interceptor خودش

    نمیدونم شاید دارم اشتباه فکر میکنم،

    برای Regsiter کردن Componentها با Type یکسان باید با نام های جدا Register کنیم (اگر دست بگم)

    به نظرم اینجا هم صورت مسئله تقریبا مشابه است با این تفاوت که ارتباط کلاس Interceptor با کلاس Application از طریق IOC ایجاد میشود.

    در Castle Windsor دو تا Interface:
    Castle.DynamicProxy.IInterceptorSelector
    و
    Castle.MicroKernel.Proxy.IModelInterceptorsSelector
    هم وجود دارند ولی نمیدونم ارتباطی با این صورت مسئله دارند یا خیر.

    ممنون میشم اگر نظرتون رو بدونم

  6. سلام مجدد

    استاد فکر کنم راهش رو پیدا کردم

    موقع معرفی Interceptor به یک Component (نام Interceptor هم میخوام وارد کنم تا وصل شود به Interceptor خودش) باید از دستور زیر استفاده کنیم:

    Interceptors(InterceptorReference.ForKey(“registered interceptor named”).Anywhere

    ولی زمانی که میخوام Component مورد نظر رو دسته ای Register کنم مجبورم Interceptors رو تو متد Configure بنویسم و اگر آخرش اون Anywhere رو بنویسم Compile Error میده

    اگر هم Anywhere رو ننویسم Interceptor اصلا عمل نمیکنه

    ولی زمانی که دسته ای Register نمیکنم چون به نوشتن Anywhere گیر نمیده در نتیجه همه چیز Ok میشه

    میبخشید زیادی نوشتم، وقت تو گرفتم

    • هادی احمدی

      سلام مهندس

      نیازی به استفاده از Anywhere نیست. سناریویی که مدنظر شما هست با Name های متفاوت برای Interceptor ها و Dependent کردن Application Service ها به این نام ها، قابل انجام هست. چون توضیحش اینجا سخت بود، نمونه کد رو براتون آپلود کردم :

      دانلود نمونه کد

      موفق باشید

      • سلام هادی جان

        ممنون از وقتی که گذاشتی برای پاسخ به سوالم، من ۳،۴ روز شهرستان بودم، دسترسی به کامپیوتر و نت نداشتم، بخاطر همین امروز موفق شدم پاسخ شما رو ببینم

        میبخشید نمونه کد رو دانلود کردم ولی کانفیگ Castle داخلش نبود، پروژه کلاینتش لود نشد.

        خیلی خیلی ممنون ازت.

      • ممنون از کمکت هادی جان

  7. سلام استاد خسته نباشید
    استاد تو دات نت core
    LifestylePerWebRequest به خاطر نبود System.Web در قابلیت های Castle وجود نداره
    جایگزینی LifestylePerWebRequest میدونید چی هست؟

دادن پاسخ بههادی احمدی بی خیال پاسخ!

آدرس ایمیلتان منتشر نمیشودگزینه های الزامی ستاره دار شده اند *

*

شما می‌توانید از این دستورات HTML استفاده کنید: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">

رفتن به بالا