تزریق وابستگی در کنترلرهای MVC

در این مقاله قصد داریم چگونگی تزریق یک شی داخل کنترلر درmvc را بررسی کنیم ،که در زیر با یک مثال آن را پیاده سازی میکنیم.

تزریق وابستگی در کنترلرهای MVC

برای انجام این کار ما از کلاس DefaultControllerFactory یا اینترفیس IControllerFactory  استفاده میکنیم که به ما در ساخت کنترلر factory کمک میکند.

به صورت پیش فرض ASP.NET MVCیک کنترلر با constructor بدون پارامتر میسازد که این سازنده از تزریق(injection) در کلاس جلوگیری میکند.

تزرریق وابستگی کنترلر یک روشی است که این کار را مقدور میکند.

برای تزریق کلاس هایمان به کنترلر  ASP.NET MVC میتوانیم دو روش زیر را دنبال کنیم :

1- Overide کردن کلاس  DefaultControllerFactory.

2- پیاده سازی متد های اینترفیس IControllerFactory.

هدف از تزریق وابستگی در کنترلر MVC رسیدن به جایی است که ما کلاس والد را به سازنده کنترلر پاس دهیم بنابراین ما میتوانیم این متد ها را در action method استفاده کنیم.

در این مورد ما از متد های ()getName و ()GetLoginDetails کلاس والد در متد DefaultControllerFactoryMethod استفاده میکنیم.

public class HomeController : Controller
    {
        private readonly IParent _iparent;
        public HomeController()
        {

        }

        public HomeController(IParent parent)
        {
            _iparent = parent;
        }

        public ActionResult DefaultControllerFactoryMethod()
        {
            ViewBag.Name = _iparent.GetName();
            ViewBag.LoginDetails = _iparent.GetLoginDetails("MyUsreName");
            return View();
        }

راه اول :  Overide کردن کلاس  DefaultControllerFactory. برای رسیدن به تزریق وابستگی 

 در این عملکرد ، ما کنترلر کلاس factory خودمان را توسط ارث بری از کلاس DefaultControllerFactory که قسمتی از فریمورک ASP.NET MVC است میسازیم ،و سپس متد ()GetcontrollerInstance  را Override میکنیم.

با دقت به کد زیر نگاه کنید ، در متد GetcontrollerInstance  ما یک نمونه از کلاس والد که به متد Activator.CreateInstance پاس شده است تنها زمانی که نام کنترلر  "Home" است میسازیم در غیر این صورت سازنده پیش فرض کنترلر بدون پارامتر اجرا میشود.

public class MyCustomDefaultControllerFactory : DefaultControllerFactory
    {
        protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
        {
            IParent parent = new Parent();

            // here we can find out the controller name and based on that instantiate class that inherits IParent interface and pass into the CreateInstance method
            var controllername = requestContext.RouteData.Values["controller"].ToString();

            IController controller = null;
            if (controllername == "Home")
            {
                controller = Activator.CreateInstance(controllerType, new[] { parent }) as Controller;
            }
            else
            {
                controller = Activator.CreateInstance(controllerType) as Controller;
            }
            
            return controller;
        }

        public override void ReleaseController(IController controller)
        {
            IDisposable dispose = controller as IDisposable; 
            if (dispose != null)
            {
                dispose.Dispose();
            }
        }
    }

را حل دوم : پیاده سازی متد های اینترفیس IControllerFactory بر ای رسیدن به تزریق وابستگی 

ساخت یک کلاس جدید که از اینترفیس IControllerFactory ارث بری کرده است و متد های ان را پیاده سازی کرده است.

در اینجا ما کلاس MyCustomControllerFactory را ساخته ایم که از آن اینترفیس ارث بری کرده است.

در وهله ی اول ما متد CreateController این اینترفیس را پیاده سازی کرده ایم که نام کنترلر و نوع برگشتی کنترلر را که به statment های switch case ما بستگی دارد را بررسی میکند.

public class MyCustomControllerFactory : IControllerFactory
    {
        
        public IController CreateController(System.Web.Routing.RequestContext requestContext, string controllerName)
        {
            IController controller = null;
            Type type = null;
            switch (controllerName)
            {
                case "Account" :
                    type = typeof(AccountController);
                    break;
                case "Manage" :
                    type = typeof(AccountController); // change to see
                    break;
                case "Another":
                    type = typeof(HomeController);
                    break;
                default:
                    type = typeof(HomeController);
                    break;
            }

            // NOTE: can instantiate an interface or class that can be passed in the CreateInstance method
            IParent parent = new Parent();

            controller = (IController)Activator.CreateInstance(type, parent);
            return controller;
        }

        public System.Web.SessionState.SessionStateBehavior GetControllerSessionBehavior(System.Web.Routing.RequestContext requestContext, string controllerName)
        {
            System.Web.SessionState.SessionStateBehavior sessionState = new System.Web.SessionState.SessionStateBehavior();
            switch (controllerName)
            {
                case "Account":
                    sessionState =  System.Web.SessionState.SessionStateBehavior.Default;
                    break;
                case "Manage":
                    sessionState = System.Web.SessionState.SessionStateBehavior.Required;
                    break;
                case "Home":
                    sessionState = System.Web.SessionState.SessionStateBehavior.ReadOnly;
                    break;
                default:
                    sessionState = System.Web.SessionState.SessionStateBehavior.Default;
                    break;
            }
            return sessionState;
        }

        public void ReleaseController(IController controller)
        {
            IDisposable disposeMe = controller as IDisposable;
            if (disposeMe != null)
            {
                disposeMe.Dispose();
            }
        }
    }

در مرحله بعدی ما نیاز به پیاده سازی متد GetControllerSessionBehavior که به ما اجازه میدهد ما برای هر کنترلر چه نوع رفتاری را نشان دهیم.

و در آخر متد ()Dispose را در کنترلر فراخوانی میکنیم.

 در صورت استفاده از راه حل اول ما نیاز به ثبت این کنترلر کلاس factoty درGlobal.asax Application_Start event داریم.

protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);

           // register the custom controller factory now 
           ControllerBuilder.Current.SetControllerFactory(new MyCustomControllerFactory());

        }