《Pro ASP.NET MVC 3 Framework》学习笔记之九【Ninject的使用-下】

接着上次的Ninject的笔记,如果你是初次路过,可以先看看我前面的笔记。

一,创建依赖链(Chains of Dependency)

当我们向Ninject请求创建一个类型时,Ninject会去检查该类型和其他类型之间的耦合关系。如果有额外的依赖,Ninject也会解析它们并创建我们需要的所有类的实例。为了进一步说明,我们创建一个新的接口和一个实现该接口的类。请注意我们的例子是跟前面的笔记衔接的,所以如果你打算跟着一起操作的话,最好能够去看看前面的笔记。

创建一个IDiscountHelper如下所示:

public interface IDiscountHelper {     //Product打折应用decimal ApplyDiscount(decimal totalParam); }

创建一个实现IDiscountHelper的类如下所示:

public class DefaultDiscountHelper : IDiscountHelper {public decimal ApplyDiscount(decimal totalParam)     { return (totalParam - (10m / 100m * totalParam)); //打9折    } } 

下面在LinqValueCalculator.cs添加依赖(这个类是前面的笔记里面的)

public class LinqValueCalculator : IValueCalculator { private IDiscountHelper discounter; 

public LinqValueCalculator(IDiscountHelper discountParam)     {         discounter = discountParam;     } 

public decimal ValueProducts(params Product[] products)     { return discounter.ApplyDiscount(products.Sum(p => p.Price));     } }

同样是通过Constructor Injection的方式

下面采取同样的方式将接口绑定到实现,如下所示:

        static void Main(string[] args)        {            IKernel ninjectKernel = new StandardKernel();//绑定接口到其对应的实现            ninjectKernel.Bind<IValueCalculator>().To<LinqValueCalculator>();            ninjectKernel.Bind<IDiscountHelper>().To<DefaultDiscountHelper>();

//获取接口实现            IValueCalculator calcuImpl = ninjectKernel.Get<IValueCalculator>();

ShoppingCart cart = new ShoppingCart(calcuImpl);

//执行            Console.WriteLine("Tatol:{0:c}", cart.CalculateStockValue());

}

我们使用Ninject将两个接口分别绑定到对应的实现类,我们不必改变实现IValueCalculator类的任何代码。Ninject知道当我们请求IValueCalculator的类型时就实例化LinqValueCalculator对象返回给我们。它会去检查这个类,并发现LinqValueCalculator依赖另一个接口,并且这个接口是它能解析的,Ninject创建一个DefaultDiscountHelper的实例注入LinqValueCalculator类的构造器,并返回IValueCalculator类型的对象。Ninject会只用这种方式检查每一个要实例化的类的依赖,不管涉及的依赖链有多复杂,多冗长。

二,指定属性和参数值

1.我们可以提供一些属性的具体值或者详细信息给Ninject,当我们要绑定接口到实现的时候。上面的例子里面我的打折9折,而且是写死的。现在我们对其进行修改,如下所示:

public class DefaultDiscountHelper : IDiscountHelper { public decimal DiscountSize { get; set; } 

public decimal ApplyDiscount(decimal totalParam)     { return (totalParam - (DiscountSize / 100m * totalParam));     } } 

这里我们定义了一个属性来表示具体的折扣。当我们再来绑定这个具体的类时,我们可以进行下面的操作.如下所示:

        static void Main(string[] args)        {            IKernel ninjectKernel = new StandardKernel();//绑定接口到其对应的实现            ninjectKernel.Bind<IValueCalculator>().To<LinqValueCalculator>();//ninjectKernel.Bind<IDiscountHelper>().To<DefaultDiscountHelper>();            ninjectKernel.Bind<IDiscountHelper>().To<DefaultDiscountHelper>().WithPropertyValue("DiscountSize", 50m);//打5折哈

//获取接口实现            IValueCalculator calcuImpl = ninjectKernel.Get<IValueCalculator>();

ShoppingCart cart = new ShoppingCart(calcuImpl);

//执行            Console.WriteLine("Tatol:{0:c}", cart.CalculateStockValue());

}

当然如果你有多个属性,可以接用"."链式的添加。

 

2.上面是通过定义一个属性来实现折扣,我们也可以通过定义在构造器里的参数实现,

如下所示:

public class DefaultDiscountHelper : IDiscountHelper {     private decimal discountRate; 

    public DefaultDiscountHelper(decimal discountParam)  {  discountRate = discountParam;  } 

public decimal ApplyDiscount(decimal totalParam)     { return (totalParam - (discountRate/ 100m * totalParam));     } } 

这个时候我们绑定的时候是这样的:ninjectKernel.Bind<IDiscountHelper>().To< DefaultDiscountHelper>().WithConstructorArgument("discountParam", 50M);
同样我们如果有多个构造器参数,也采用"."的方式链式添加。

3.使用Self-Binding

一个非常有用的功能:Self-Binding(自我绑定)能够完全将Ninject整合到我们的代码里面,也就是来自Ninject内核被请求的类的地方。这可能看起来有点奇怪,这也意味着我们不用手动的执行DI的初始化。向下面这个地方:

IValueCalculator calcImpl = ninjectKernel.Get<IValueCalculator>(); ShoppingCart cart = new ShoppingCart(calcImpl); 

我们可以简单的请求一个ShoppingCart的实例并让Ninject挑选出对IValueCalculator依赖的类。

通过这种方式:ShoppingCart cart = ninjectKernel.Get<ShoppingCart>();
Self-Binding到一个具体的类,通过这种方式:ninjectKernel.Bind<ShoppingCart>().ToSelf().WithParameter(Parameter);具体如下所示:

        static void Main(string[] args)        {            IKernel ninjectKernel = new StandardKernel();//绑定接口到其对应的实现            ninjectKernel.Bind<IValueCalculator>().To<LinqValueCalculator>();//ninjectKernel.Bind<IDiscountHelper>().To<DefaultDiscountHelper>();            ninjectKernel.Bind<IDiscountHelper>().To<DefaultDiscountHelper>().WithPropertyValue("DiscountSize", 50m);

//获取接口实现//IValueCalculator calcuImpl = ninjectKernel.Get<IValueCalculator>();

//ShoppingCart cart = new ShoppingCart(calcuImpl);

 ninjectKernel.Bind<ShoppingCart>().ToSelf();//如果注释掉这行也不影响程序运行,我还不知道原因,有路过的大牛请指导下。

 ShoppingCart cart = ninjectKernel.Get<ShoppingCart>();

//执行            Console.WriteLine("Tatol:{0:c}", cart.CalculateStockValue());

}

 

4.绑定到派生类型(Binding to a Derived Type)

尽管我们前面一直关注的是绑定接口(因为接口跟MVC里面的相关性更强),我们也可以使用Ninject绑定具体的类,Self-Binding就是这样做的。我们也可以使用Ninject绑定一个具体的类到它的派生类。我想这里也好理解,因为我们绑定接口到实现它的类--类继承了该接口,这里是类继承了类罢了。

下面我们修改ShoppingCart类,使其能够支持简单的派生l类--LimitShoppingCart.提供一个限制价格上限的属性ItemLimit。修改后的ShoppingCart类如下所示:

public class ShoppingCart { protected IValueCalculator calculator; protected Product[] products; public ShoppingCart(IValueCalculator calcParam)     {         calculator = calcParam;         products = new[] { new Product() { Name = "Kayak", Price = 275M}, new Product() { Name = "Lifejacket", Price = 48.95M}, new Product() { Name = "Soccer ball", Price = 19.50M}, new Product() { Name = "Stadium", Price = 79500M}         };      } public virtual decimal CalculateStockValue() //定义为虚方法,在派生类里面重写该方法    {         decimal totalValue = calculator.ValueProducts(products);         return totalValue;     } } 

派生类LimitShoppingCart如下所示:

public class LimitShoppingCart : ShoppingCart { 

public LimitShoppingCart(IValueCalculator calcParam)         : base(calcParam)     { //调用基类的构造器,为的是在初始话LimitShoppingCart时首先调用ShoppingCart的构造器        //因为我们要使用calculator    } 

public override decimal CalculateStockValue()     {         //这里过滤掉哪些价格超过了上限的Product,任何再求和var filteredProducts = products.Where(e => e.Price < ItemLimit); return calculator.ValueProducts(filteredProducts.ToArray());     } 

public decimal ItemLimit { get; set; } } 

下面我们绑定ShoppingCart到它的派生类LimitShoppingCart,如下所示:

        static void Main(string[] args)        {            IKernel ninjectKernel = new StandardKernel();//绑定接口到其对应的实现            ninjectKernel.Bind<IValueCalculator>().To<LinqValueCalculator>();//ninjectKernel.Bind<IDiscountHelper>().To<DefaultDiscountHelper>();            ninjectKernel.Bind<IDiscountHelper>().To<DefaultDiscountHelper>().WithPropertyValue("DiscountSize", 50m);

//获取接口实现//IValueCalculator calcuImpl = ninjectKernel.Get<IValueCalculator>();

//ShoppingCart cart = new ShoppingCart(calcuImpl);

//ninjectKernel.Bind<ShoppingCart>().ToSelf();

//ShoppingCart cart = ninjectKernel.Get<ShoppingCart>();            ninjectKernel.Bind<ShoppingCart>().To<LimitShoppingCart>().WithPropertyValue("ItemLimit", 200M);            ShoppingCart cart = ninjectKernel.Get<ShoppingCart>();

//执行            Console.WriteLine("Tatol:{0:c}", cart.CalculateStockValue());

}

 

5.使用条件绑定(Conditional Binding)

有时对同一个接口会有多重实现或者是对同一个类有多个派生,这时可以指定不同的条件来说明哪一个应该被使用。下面通过具体的实例说明:
创建一个新的IValueCalculator的接口实现IterativeValueCalculator.cs如下所示:

    public class IterativeValueCalculator : IValueCalculator    {public decimal ValueProducts(params Product[] products)        {decimal totalValue = 0;foreach (Product p in products)            {                totalValue += p.Price;            }return totalValue;        }    }

现在我们有了对IValueCalculator的两个实现了,可以通过Ninject设置条件来指定哪个显示被使用。如下所示:

        static void Main(string[] args)        {            IKernel ninjectKernel = new StandardKernel();//绑定接口到其对应的实现            ninjectKernel.Bind<IValueCalculator>().To<LinqValueCalculator>();//ninjectKernel.Bind<IDiscountHelper>().To<DefaultDiscountHelper>();//ninjectKernel.Bind<IDiscountHelper>().To<DefaultDiscountHelper>().WithPropertyValue("DiscountSize", 50m);

//获取接口实现//IValueCalculator calcuImpl = ninjectKernel.Get<IValueCalculator>();//ShoppingCart cart = new ShoppingCart(calcuImpl);

//Self-Binding//ninjectKernel.Bind<ShoppingCart>().ToSelf();//ShoppingCart cart = ninjectKernel.Get<ShoppingCart>();

//Bind to Derived class//ninjectKernel.Bind<ShoppingCart>().To<LimitShoppingCart>().WithPropertyValue("ItemLimit", 200M);//ShoppingCart cart = ninjectKernel.Get<ShoppingCart>();

//Conditional Binding            ninjectKernel.Bind<IValueCalculator>().To<IterativeValueCalculator>().WhenInjectedInto<LimitShoppingCart>();            ninjectKernel.Bind<ShoppingCart>().To<LimitShoppingCart>().WithPropertyValue("ItemLimit", 200M);            ShoppingCart cart = ninjectKernel.Get<ShoppingCart>();

//执行            Console.WriteLine("Tatol:{0:c}", cart.CalculateStockValue());

}

我们可以通过断点调试运行看看是否进到了IterativeValueCalculator里面,从而判断我们设置的条件是否起到了作用。我试过了,不管你信不信,反正我是信了!嘿嘿!当然如果我们的条件没有一个合适也没有关系,Ninject会寻找一个对该类或接口的默认的绑定,以至于Ninject会有一个返回的结果。这个有点类似我们使用Switch时一般提倡加上Default也是这个道理。

下图是关于Ninject条件绑定的方法,如下所示:

另,我想说IoC容器有几种这里使用是其中的一种,大家可以自由选择一个IoC容器

6.在ASP.NET MVC中应用Ninject

之前的比较使用控制台应用程序为例介绍了Ninject的核心功能,但是要将Ninject整合到ASP.NET MVC中并不是太容易。首先我们需要创建一个类继承自 System.Web.Mvc.DefaultControllerFactory,这个类是MVC Framework默认用来创建controller实例的类(当然在书的后面的章节会介绍如何创建自定义的实现来替代default controller factory).这里我们的实现如下所示:

    public class NinjectControllerFactory : DefaultControllerFactory     { private IKernel ninjectKernel; 

public NinjectControllerFactory()        {              ninjectKernel = new StandardKernel();              AddBindings();          } 

protected override IController GetControllerInstance(RequestContext requestContext,              Type controllerType)         { return controllerType == null                  ? null                  : (IController)ninjectKernel.Get(controllerType);          } 

private void AddBindings()         {             ninjectKernel.Bind<IProductRepository>().To<FakeProductRepository>();         }     } 

这个类创建了Ninject的核心,通过GetControllerInstance()方法为controller classes的请求服务,这个方法在需要一个controller对象的时候被MVC框架调用。我们不必使用Ninject显示的绑定controller classes。由于controllers classes继承了System.Web.Mvc.Controller,我们可以依靠默认的self-binding。AddBindings()方法允许我们绑定Repositories和其他需要保持松耦合的组件。我们也可以使用这个方法绑定需要额外的构造器参数或属性参数的controller classes。

一旦我们创建了这个类,我们需要在MVC Framework的Global.asax里面注册。注册的方法如下:

protected void Application_Start() {     AreaRegistration.RegisterAllAreas(); 

RegisterGlobalFilters(GlobalFilters.Filters);     RegisterRoutes(RouteTable.Routes); 

ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory()); } 

现在MVC Framework就会使用我们的NinjectControllerFactory来获取controller classes的实例,并且Ninject会自动处理controller 对象的DI(依赖注入)。后面章节的比较会有关于这部分的例子。这里你可以先了解下大概的原理。可能到这里的时候,我们会觉得在Ninject上面花费的很多功夫,似乎跟MVC的学习不是很相关。但是我们DI的深刻的理解能够帮助我们开发和测试更加简单,容易。

好了,今天的笔记就做到这里,笔记中肯定会有我理解不准确或错误的地方,还请路过的朋友多多指导帮助,谢谢!

晚安!

时间: 2024-09-27 20:54:08

《Pro ASP.NET MVC 3 Framework》学习笔记之九【Ninject的使用-下】的相关文章

ASP.NET MVC 3 Framework学习笔记之Model Templates

.使用模板化的视图Helpers(Using Templated View Helpers) 模版化视图helpers的创意就是它们更加灵活.我们不用自己去指定应该用什么HTML元素来呈现一个模型的属性,MVC自己会搞定,在我们更新了视图模型时,也不用手动的更新视图.下面是一个例子:  代码如下 复制代码 //在Models里面添加Persons.cs using System; using System.Collections.Generic; using System.Linq; using

《Pro ASP.NET MVC 3 Framework》学习笔记目录

<Pro ASP.NET MVC 3 Framework>简介: 作者: Adam Freeman 和 Steven Sanderson 出版社: Apress; New 平装: 820页 语种: 英语 ISBN: 1430234040 声明:笔记里面按我自己的理解翻译了大部分内容,写这个笔记的目的:为了方便自己查阅,也为园友提供学习的方便. 我无意侵犯作者的任何权利,仅仅为了自己学习.也希望路过的朋友不要用于任何商业目的. 第一部分 ASP.NET MVC3介绍   <Pro ASP.

《Pro ASP.NET MVC 3 Framework》学习笔记之一【MVC的历程,优点,HelloWorld】

序论:asp.net mvc出现已经有两三年的时间了(2009开始1.0版本),但是这么方面的中文学习资料仍然非常少,特别是asp.net mvc3,几乎就没有中文的学习书籍.在英文的书籍中有两本是非常经典的mvc3教程:<Professional ASP.NET MVC 3>--作者:Jon Galloway , Phil Haack, Brad Wilson , K. Scott Allen和<Pro ASP.NET MVC 3 Framework>--作者:Steven Sa

ASP.NET MVC Web API 学习笔记----HttpClient简介

  1. HttpClient简单介绍  依稀还记得那个时候用WebClient,HttpWebRequest来发送一个请求,现在ASP.NET MVC4中自带了一个类HttpClient,用于接收HttpResponseMessage和发送HttpRequestMesssage. 问题在于既然WebClient,HttpWebRequest可以完成相应的功能,为什么还要使用HttpClient类,.NET Framework中既然提出了这样一个类肯定是有其特别之处的,这里罗列几个不同之处: (

ASP.NET MVC Web API 学习笔记---联系人增删改查

本章节简单介绍一下使用ASP.NET MVC Web API 做增删改查.目前很多Http服务还是通过REST或者类似RESP的模型来进行数据操作的.下面我们通过创建一个简单的Web API来管理联系人          说明:为了方便数据不使用真正的数据库,而是通过内存数据模拟    1.       Web API中包含的方法 Action HTTP method Relative URI GetAllContact GET /api/contact GetContact GET /api/

《Pro ASP.NET MVC 3 Framework》学习笔记之四【领域模型介绍】

主题:应用领域驱动开发(Applying Domain-Driven Development) Domain Model是MVC程序的"心脏",其他的一切,包括Controllers和Views仅仅是用来跟Domain Model交互的一种方式,ASP.NET MVC并没有限制使用在Domain Model上面的技术,我们可以自由的选择跟.net framework交互的技术,并且这样的选择是非常多的.不仅如此,ASP.NET MVC为我们提供了基础的架构和约定来帮助Domain Mo

《Pro ASP.NET MVC 3 Framework》学习笔记之五【依赖注入及ninject工具使用】

一,创建松耦合的组件 1."分解关注点"是MVC模式里面一个非常重要的特性.我们想要在应用程序里面创建的组件尽可能的独立,这样我们就能管理比较少的依赖关系.理想情况下,每个组件都是孤立的,不知道其他组件的存在,处理应用程序的其他领域仅仅通过抽象接口,这就是所谓的松耦合,它让我们的应用程序更加容易测试和修改.通过一个简单的例子可以帮助我们理解,假如我们想写一个发邮件的组件,暂且就把这个组件命名为MyEmailSender,接着我们实现一个接口,这个接口定义了所有需要发送邮件的功能,也暂且

《Pro ASP.NET MVC 3 Framework》学习笔记之三十五 【部署】

准备要部署的应用程序 在正式进入部署MVC程序到IIS之前,会介绍一些关于应用程序迁移到生产环境之前探测错误以及一旦进入生产环境最大化性能的技术.同时也会展示关于流线型部署过程的有用的功能.   检测视图错误 Razor视图会在服务器需要的时候编译而不是在VS里面生成项目时编译,正常情况下,探测视图编译错误的方式是系统的访问每一个action,从而让每一个view都能够呈现.这显然是非常乏味而且不会一直成功的技术,特别是在基于不同的model状态呈现不同的view的时候.我们可以启用一个特别的项

《Pro ASP.NET MVC 3 Framework》学习笔记之十五【示例项目SportsStore】

绑定Shopping Cart 定义购物车Cart的实体,购物车是我们程序业务领域的一个部分,所以在我们领域模型(Domain Model)里面添加一个cart的实体是合理的.在SportsStore.Domain的Entities文件夹下添加一个Cart的实体类,如下所示: View Code public class Cart {private List<CartLine> lineCollection = new List<CartLine>();//添加 public vo