您的位置:首页 > 编程语言 > ASP

【Pro ASP.NET MVC 3 Framework】.学习笔记.2.MVC的主要工具-Ninject

2013-08-22 11:44 627 查看
这三个工具,应该是每个MVC程序员的兵工厂中的一部分。DI容器,单元测试框架,mocking 工具。Ninject是我们偏爱的DI容器,它简单,高雅,并且容易使用。这里有很多复杂的替代品,但是我们喜欢Ninject最小配置的工作方式。如果你不喜欢Ninject,可以使用Unity,它是微软提供的替代品。

单元测试方面,我们使用VS2010内置的 NUnit,它是.Net 单元测试框架中最受欢迎的一个。

Mocking 工具套装,我们选择 Moq。如果你不喜欢它,可以使用Rhino Mocks这个不错的替代品。

1 使用 Ninject

在原书中第四章,The MVC Pattern 中,我们介绍过DI的思想,用来在我们的MVC程序中的组件解耦。为了做到这点,我们需要结合接口和DI。

class Program { staticvoid Main(string[] args) { ShoppingCart cart =new ShoppingCart(new LinqValueCalculator()); Console.WriteLine("Total:{0}", cart.CalculateStockValue()); Console.ReadKey(); } } publicclass Product { privatestring name; publicint ProductID { get; set; } publicstring Name { get { return name; } set { name = value; } } publicstring Description { get; set; } publicdecimal Price { get; set; } publicstring Category { get; set; } } publicinterface IValueCalculator { decimal ValueProducts(params Product[] products); } publicclass LinqValueCalculator : IValueCalculator { publicdecimal ValueProducts(params Product[] products) { return products.Sum(prod => prod.Price); } } publicclass ShoppingCart { private IValueCalculator calculator; public ShoppingCart(IValueCalculator calcParam) { calculator = calcParam; } publicdecimal CalculateStockValue() { Product[] products = { 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} }; decimal totalValue = calculator.ValueProducts(products); return totalValue; } }

IValueCalculator 接口中定义了一个方法,它接受一个或多个Product对象,并返回累积的值。我们将接口部署在 LinqValueCalculator类上,它使用Linq延期方法Sum,灵巧地生成Product对象们的Price属性合计。然后,我们需要创建一个会使用到IVaueCalculator的类,即ShoppingCart,这个类是为DI设计的。Shoppingcart类的构造器需要一个实现了IValueCalculator接口的类,作为参数,为DI做准备。CalculateStockValue方法创建一个Product对象数组,然后调用IValueCalculator接口中的ValueProducts,来得到合计。

我们成功地将ShoppingCart类和LinqValueCalculator类解耦,这两个类都依赖IValueCalculator,但是ShppingCart与LinqValueCalculator没有直接关系。事实上,它甚至不知道LinqValueCalculator的存在。我们能改变LinqValueCalculator的实现,甚至用一个新的IValuecalculator的实现完全替代它。

Product类与这三个类有直接关系,我们不用担心这点。Product 是一个 domain model 类型的等价物,我们期望这样的类,强耦合,依赖我们的程序。如果我们不构建MVC程序,我们也许持不同的观点,并解耦Product。

我们的目标是能够创建一个ShoppingCart的实例,并注入一个IValueCalculator类的实现,作为构造函数的参数。这是我们偏爱的DI容器Ninject所扮演的角色。但是在我们展示Ninject之前,我们需要设置VS。

1.1 使用Ninject开始

要开始使用Ninject,我们需要创建一个Ninject kernel的实例,这个对象,我们会用来与Ninject交流。

IKernel ninjectKernel =new StandardKernel();

一旦创建kernel,Ninject会完成两个阶段的工作。第一是绑定你想要使用你已经创建的接口关联的类型。在这种情况下,我们想要告诉Ninject,当它收到一个请求,请求IValueCalculator的实例时,它应该创建并返回一个LinqValueCalculator类的实例。我们用定义在IKernel接口中的Bind和To方法做这样的事情。

ninjectKernel.Bind<IValueCalculator>().To<LinqValueCalculator<();

这段声明将IValueCalculator接口绑定到LinqValueCalculator实例类上。我们指定我们想要注册的接口,将它作为Bind方法的一般类型参数,并传递我们想要的具体实例的类型,作为第二个参数。

第二阶段,是使用Ninject的Get方法,创建一个实施接口的对象,并将它传递给ShoppingCart类的构造器。

IValueCalculator calcImpl = ninjectKernel.Get<IValueCalculator>(); ShoppingCart cart =new ShoppingCart(calcImpl); Console.WriteLine("Total:{0:c}", cart.TotalPrice());

我们指定我们想要实例化的接口,作为Get方法的一般类型参数。Ninject浏览我们定义的绑定,看到我们将IValueCalculator绑定到LinqValueCalculator,然后为我们创建一个新的实例。我们然后将实例注入到ShoppingCart类的构造器,并调用TotalPrice方法,它会反过来调用接口中定义的方法。

ShoppingCart cart =new ShoppingCart(new LinqValueCalculator());

可以简化为这样。

1.2 创建依赖链

当我们请求Ninject创建一个类型,它会检查类型之间的耦合。如果有附加选项,Ninject解决他们,并创建所有必须的类的实例。

publicinterface IDiscountHelper { decimal ApplyDiscount(decimal totalParam); } publicclass DefaultDiscountHelper : IDiscountHelper { publicdecimal ApplyDiscount(decimal totalParam) { return (totalParam - (10m / 100m * totalParam)); } }

IDiscounHelper定义了一个ApplyDiscount方法,它会应用一个decima值折扣。DefaultDiscounterHelper类实现这个接口。我们可以将IDiscountHelper借口添加为LinqValueCalculator的依赖。

publicclass LinqValueCalculator : IValueCalculator { private IDiscountHelper discounter; public LinqValueCalculator(IDiscountHelper discountParam) { discounter = discountParam; } publicdecimal ValueProducts(params Product[] products) { return discounter.ApplyDiscount(products.Sum(prod => prod.Price)); } }

最新添加的构造器,需要传递一个IDiscountHelper接口的实现,它被用在ValueProducts方法,在处理累积Product对象的值时,应用打折。我们使用Ninject kernel将IDiscountHelper接口绑定到类的实现上。

ninjectKernel.Bind<IValueCalculator>().To<LinqValueCalculator>(); ninjectKernel.Bind<IDiscountHelper>().To<DefaultDiscountHelper>(); IValueCalculator calImpl = ninjectKernel.Get<IValueCalculator>(); ShoppingCart cart =new ShoppingCart(calImpl);

我们不用不用改变任何代码来创建IValueCalculator的实现。当需要IValueCalculator时,Ninject知道我们想要LinqValueCalculator类被实例化。他已经检验过这个类,并发现它基于一个接口实现。Ninject创建一个DefaultDiscountHelper的实例,将它注入到LinqValueCalculator类的构造器,并将结果作为IValueCalculator返回。Ninject检查所有用这种方式实例化依赖的类,无论它的依赖链有多长或多复杂。

1.3 指定属性和参数的值

我们能配置Ninject创建的类,来提供当我们将接口绑定到它的实现上时的属性细节。我们修正了StandardDiscountHelper类,使它暴漏一个方便的属性,来指定折扣的程度。

publicclass DefaultDiscountHelper : IDiscountHelper { publicdecimal DiscountSize { get; set; } publicdecimal ApplyDiscount(decimal totalParam) { return (totalParam - (DiscountSize / 100m * totalParam)); } }

当我们使用Ninject将具体的类绑定到类型,我们可以使用WithPropertyValue方法,设置DefaultDiscountHelper类中DiscountSize属性的值。

ninjectKernel.Bind<IDiscountHelper>().To<DefaultDiscountHelper>().WithPropertyValue("DiscountSize",50M);

我们必须以字符串的形式提供属性的名字。我们不用改变任何其他的绑定,也不用改变Get方法的使用方式。属性的值,会随着DefaultDiscountHelper构建时设置。

如果你有多个值需要设置,可以链式调用WithPropertyValue方法。

也可以给构造函数传递参数

publicdecimal discountRate; public DefaultDiscountHelper(decimal discountParam) { discountRate = discountParam; } ninjectKernel.Bind<IDiscountHelper>().To<DefaultDiscountHelper>().WithConstructorArgument("discountParam", 50M);

1.4 使用自绑定

自绑定,是将Ninject完全整合进你代码的一个有用的特性,具体的类能从Ninject kernel请求。这看起来像是在做一件无聊的事情,但意味着我们不需要像以下代码那样,手工执行初始化DI。

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

而是可以简单地请求一个ShoppingCart实例,让Ninject挑选出依赖于IValueCalculator类。

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

如果我们花时间注册一个自绑定类型,我们能在接口上使用这些特性,像为构造器参数和属性指定值。要注册自绑定,偶们使用ToSelf方法

ninjectKernel.Bind<ShoppingCart>().ToSelf().WithParameter("<parameterName>", <paramvalue>);

ShoppingCart绑定自身,调用WithParameter方法,为虚构的属性提供值。你可以仅在具体的类上使用自绑定。

1.5 绑定到派生类型

尽管我们关注接口,我们也使用Ninject绑定具体的类。我们既能绑定具体的类自己,也能绑定到一个派生类型。

publicclass ShoppingCart { protected IValueCalculator calculator; protected Product[] products; public ShoppingCart(IValueCalculator calcParam) { calculator = calcParam; products =new[] { new Product{Name="Jiangyou",Price=5M}, new Product{Name="Zhijin",Price=2.5M} }; } publicvirtualdecimal CalculateStockValue() { decimal total = calculator.ValueProducts(products); return total; } } publicclass LimitShoppingCart : ShoppingCart { public LimitShoppingCart(IValueCalculator calcParam): base(calcParam) { // } publicoverridedecimal CalculateStockValue() { var filteredProducts = products.Where(e => e.Price < ItemLimit); return calculator.ValueProducts(filteredProducts.ToArray()); } publicdecimal ItemLimit { get; set; } } staticvoid Main(string[] args) { IKernel ninjectKernel =new StandardKernel(); ninjectKernel.Bind<IValueCalculator>().To<LinqValueCalculator>(); ninjectKernel.Bind<IDiscountHelper>().To<DefaultDiscountHelper>().WithConstructorArgument("discountParam", 50M); ninjectKernel.Bind<ShoppingCart>().To<LimitShoppingCart>().WithPropertyValue("ItemLimit", 3M); ShoppingCart cart = ninjectKernel.Get<ShoppingCart>(); Console.WriteLine(cart.CalculateStockValue()); Console.ReadKey(); }

我们能绑定父类,这样当我们从Ninject请求一个它的实例时,派生类的一个实例会被创建。这个技术用来绑定抽象类到它的具体实现时,工作的非常好。

1.6 使用条件绑定

使用Ninject,我们能绑定同一个接口的多个实现,或者同一个类的多个派生,使用指令,告诉它在不同的情况下应该使用哪个。下面我们创建一个IValueCalculator接口的新的实现:

publicclass IterativeValueCalculator : IValueCalculator { publicdecimal ValueProducts(params Product[] products) { decimal total =0; foreach (Product p in products) { total += p.Price; } return total; } } ninjectKernel.Bind<IValueCalculator>().To<IterativeValueCalculator>().WhenInjectedInto<LimitShoppingCart>();

我们对IValueCalculator有一个原始的绑定,Ninject试图找到最匹配的绑定,如果条件不满足,它会使用默认绑定,到相同的类和接口。所以Ninject有一个退回的值。最有用的条件绑定方法:

MethodEffect
When(predicate)当条件中的Lambda表达式等于true时
WhenClassHas<T>()当类注入含有一个指定类型的属性
WhenInjectedInto<T>()当类被注入到类型T
1.7 在mvc中使用Ninject

首先要创建一个派生自System.Web.Mvc.DefaultControllerFactory的类。DefaultControllerFactory类是MVc用来默认创建controller 类的使用。

这类创建了一个Ninject kernel,并使用它来为通过GetControllerInstance方法创建的,当它想要的一个controller对象时,被MVC框架调用的controller类,的请求服务。我们不需要使用Ninject明确地绑定controller类。我们依靠默认的自绑定特性,自从controller成为System.Web.Mvc.Controller派生的一个具体类。

AddBindings方法,允许我们为想要保持低耦合的套件和其他组件添加其他的Ninject绑定。我们也能使用这个方法,做诶一个绑定controller类的时机——需要附加构造器参数或属性值。

一旦我们创建了这个类,我们必须使用MVC框架注册它。

现在,MVC框架会使用我们的NinjectControllerFactory来获得controller类的实例,Ninject会自动处理DI到controller。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐