1. 程式人生 > >IOC Of Ninject Base On ASP.NET MVC

IOC Of Ninject Base On ASP.NET MVC

dex 準備工作 應用 new 引用 cti err art part

說在之前的話

IOC的概念相信大家比較熟悉了,習慣性稱之為依賴註入或控制反轉,園子裏對基於MVC平臺IOC設計模式已經相當多了,但大家都只知道應該怎麽應用一個IOC模式,比如Ninject, Unity等常用框架,並不清楚為什麽要用它,它們之間有什麽區別,換句話說在實際應用中的橫向對比的文章比較少,本文旨在拋磚引玉,歡迎大家討論

Ninject模式實現

VS工具提供了很方便的ninject模式引入,新建一個asp.net mvc項目後,執行幾行命令就即可

1.添加Ninject。工具-->Nuget程序包管理器-->程序包管理器控制臺,輸入下面的命令:

Install-Package Ninject -version 3.0.1.10
Install-Package Ninject.Web.Common -version 3.0.0.7
Install-Package Ninject.MVC3 -Version 3.0.0.6

分別引用了

Minject

Ninject.Web.Common

Ninject.Web.MVC

Ninject模式下,有兩種常用的方式實現,一種是通過Activitor激活Controller的方式,另一種方式MVC5推薦的DependencyResolver方式

首先做好準備工作,假設有一個Employee實體類

public class Employee
    {
        [Display(Name="ID")]
        public string Id { get; private set; }
        [Display(Name = "姓名")]
        
public string Name { get; private set; } [Display(Name = "性別")] public string Gender { get; private set; } [Display(Name = "出生日期")] [DataType(DataType.Date)] public DateTime BirthDate { get; private set; } [Display(Name = "部門")] public string Department { get
; private set; } public Employee(string id, string name, string gender, DateTime birthDate, string department) { this.Id = id; this.Name = name; this.Gender = gender; this.BirthDate = birthDate; this.Department = department; } }

對實現類提供的操作服務

public interface IEmployeeRepository
    {
        IEnumerable<Employee> GetEmployees(string id = "");
    }

添加對服務的實現

public class EmployeeRepository: IEmployeeRepository
    {
        private static IList<Employee> employees;
        static EmployeeRepository()
        {
            employees = new List<Employee>();
            employees.Add(new Employee(Guid.NewGuid().ToString(), "Employee1", "", new DateTime(1987, 8, 24), "銷售部"));
            employees.Add(new Employee(Guid.NewGuid().ToString(), "Employee2", "", new DateTime(1991, 7, 10), "人事部"));
            employees.Add(new Employee(Guid.NewGuid().ToString(), "Employee3", "", new DateTime(1993, 9, 21), "財務部"));
        }
        public IEnumerable<Employee> GetEmployees(string id = "")
        {
            return employees.Where(e => e.Id == id || string.IsNullOrEmpty(id) || id == "*");
        }
    }

1.看看最新的DependencyResolver方式的實現,新建類 NinjectDependencyResolver 來實現IDependencyResolver的GetService和GetSerivices兩個接口,

註意這裏的Bind的方法,這裏是為Controller註冊Service的核心代碼,所有Controller中以來的Ninject接口服務都在這裏註冊(接口與實現分離,Ninject托管)

public class NinjectDependencyResolver : IDependencyResolver
    {
        private IKernel kernel;

        public NinjectDependencyResolver()
        {
            kernel = new StandardKernel();
            Bind();
        }

        public NinjectDependencyResolver(IKernel kernelParam)
        {
            kernel = kernelParam;
            Bind();
        }

        public object GetService(Type serviceType)
        {
            return kernel.TryGet(serviceType);
        }

        public IEnumerable<object> GetServices(Type serviceType)
        {
            return kernel.GetAll(serviceType);
        }

        private void Bind()
        {
            this.kernel.Bind<IEmployeeRepository>().To<EmployeeRepository>();
        }

上面已經註冊了服務,現在需要把註冊信息告訴Controller的Builder工廠;MVC5把這個工作已經自動化了,當最上面引入Ninject.Web.Common的時候,已經在App_Start文件夾下自動生成了NinjectWebCommon,註意RegisterServices方法需要我們主動將上面已經寫好的註冊類NinjectDependencyResolver初始化給Mvc.DependencyResolver對象,大家不難發現這裏是對當前程序的全局設置,事實上NinjectWebCommon的Start方法比Global中Application_Start方法更早的執行,已便對整個應用程序做好初始化

public static class NinjectWebCommon 
    {
        private static readonly Bootstrapper bootstrapper = new Bootstrapper();

        /// <summary>
        /// Starts the application
        /// </summary>
        public static void Start() 
        {
            DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
            DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
            bootstrapper.Initialize(CreateKernel);
        }
        
        /// <summary>
        /// Stops the application.
        /// </summary>
        public static void Stop()
        {
            bootstrapper.ShutDown();
        }
        
        /// <summary>
        /// Creates the kernel that will manage your application.
        /// </summary>
        /// <returns>The created kernel.</returns>
        private static IKernel CreateKernel()
        {
            var kernel = new StandardKernel();
            kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
            kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
            
            RegisterServices(kernel);
            return kernel;
        }

        /// <summary>
        /// Load your modules or register your services here!
        /// </summary>
        /// <param name="kernel">The kernel.</param>
        private static void RegisterServices(IKernel kernel)
        {
            System.Web.Mvc.DependencyResolver.SetResolver(new NinjectDependencyResolver(kernel));
        }        
    }

到此Ninject已經引入完成了,怎麽使用呢

下面添加一個對Employee操作的Controller, 聲明一個IEmployeeRepository服務,註意這裏是接口,在上面打上[Inject]標記後,在Action方法中就可以隨意使用Employee服務了。可能有人疑惑這裏的IEmployeeRepository沒實例化如何就能使用,其實上面實現的RegisterServices方法中,已經對ControllerBuilder的初始化了註冊信息,所有實現Controller的子類在實例化時根據[Inject]標記查找註冊信息中的實現類進行初始化服務,在Action中引用的已經是實例化好的服務實體了

public class EmployeeController : Controller
    {
        [Inject]
        public IEmployeeRepository Repository { get; set; }

        public ActionResult Index()
        {
            var employees = this.Repository.GetEmployees();
            return View(employees);
        }

        public ActionResult Detail(string id)
        {
            Employee employee = this.Repository.GetEmployees(id).FirstOrDefault();
            if (null == employee)
            {
                throw new HttpException(404, string.Format("ID為{0}的員工不存在", id));
            }
            return View(employee);
        }
    }

讀到這算是知其然了,感覺很麻煩,為什麽不直接在controller裏new一個EmployeeRepository,接口都省了,想到這點說明你很勤於思考,回答這點需要說下為什麽要引入Ninject,或者說IOC有什麽好處,多做了那麽多復雜的動作,好處呢

1. 這裏托管給Jinject的服務實例會根據訪問量最大限度的節約實例創建,如果省了這些,我們不得不在每個controller中保留要依賴的服務對象占用資源

2.controller中並沒有直接對服務的實現類依賴,而是引用的接口,也就是說,controller開發者不需要關心服務是否已經現實,只在接口已經聲明就可以開發,這即是實現當下最流行的敏捷開發的基礎,後端服務的實現和前端的交互可同時進行(註意這裏的接口集應該獨立在新建的程序集中而不是放在MVC應用程序集,後面基於IOC框架設計再擴展)

3.MVC應用程序使用的所有服務都托管在Ninject而不是散落在controller(甚至到頁面上), 對於安全策略,日誌記錄,錯誤處理可以集中處理,相當於對前端有了一個總閥

4.對於後端的擴展和模塊化設計提供了便捷,這點比較寬泛

附言:

MVC5中的NinjectWebCommon類的CreateKernel方法中提供了IHttpModule的Bind,這裏為喜歡玩管道的打開了一扇窗,讓這版MVC成了一個新的過渡


2.Activitor激活實現

這裏也是在ControllerBuilder初始化時做文章

public class NinjectControllerActivator : IControllerActivator
    {
        public IKernel Kernel { get; private set; }
        public NinjectControllerActivator()
        {
            this.Kernel = new StandardKernel();
            AddBindings();
        }
        public IController Create(RequestContext requestContext, Type controllerType)
        {
            return (IController)this.Kernel.TryGet(controllerType) as IController;
        }
        private void AddBindings()
        {
            this.Kernel.Bind<IEmployeeRepository>().To<EmployeeRepository>();
        }
    }

對應在Global的Application_Start方法中是這樣告訴Builder的,這裏通過初始化CountrollerFactory對象實現服務註冊信息的註入

NinjectControllerActivator controllerActivator = new NinjectControllerActivator();
DefaultControllerFactory controllerFactory = new DefaultControllerFactory(controllerActivator);
ControllerBuilder.Current.SetControllerFactory(controllerFactory);

看到ControllerFactory可能有人會想起Unity的方式,後面將Ninject比對下Unity

參考文章:

http://www.cnblogs.com/artech/archive/2012/04/01/controller-activation-032.html

http://www.cnblogs.com/stoneniqiu/p/4594642.html

IOC Of Ninject Base On ASP.NET MVC