上一篇说了Ninject的初步使用,这里在说一下稍微复杂的使用。
@[toc] # 创建依赖项链 依赖项是什么呢?简单来说,上一篇当中Home控制器的构造器(构造函数)有IValueCalculator类型参数,那么IValueCalculator就是HomeConreoller的依赖项了。 当我们的依赖项又依赖于其他类型时,即依赖项又有依赖项时,这时Ninject又会继续解析并创建所需要的所有类的实例,一个又一个的依赖项的依赖性链接成一条依赖项链。比如,IValueCalculator的实现类LinqValueCalculator如果又一个依赖项IDiscountHelper,而IDiscountHelper的实现类是DefaultDiscountHelper类,这时就构成了一个依赖项。 为了演示这一过程,我们在Models文件夹内添加一个Discount.cs文件,修改其内容如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace EssentialTools.Models
{
public interface IDiscountHelper
{
decimal ApplyDiscount(decimal totalParam);
}
public class DefaultDiscountHelper : IDiscountHelper
{
public decimal ApplyDiscount(decimal totalParam)
{
return (totalParam - (discountSize / 100m * totalParam));
}
}
}
Discount文件内定义了IDiscountHelper接口,它的ApplyDiscount方法将一个十进制接口运用于一个十进制的数。DefaultDiscountHelper 类实现了该接口,并使用固定的10%的折扣。 然后我们为LinqValueCalculator类添加一个IDiscountHelper依赖项:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace EssensialTools.Models
{
public class LinqValueCalculator:IValueCalculator
{
private IDiscountHelper discount;
public LinqValueCalculator(IDiscountHelper discountParam)
{
discount = discountParam;
}
public decimal ValueProducts(IEnumerable<Product> products)
{
return discount.ApplyDiscount(products.Sum(p=>p.Price));
}
}
}
此时向之前做的那样,将DefaultDiscountHelper 和IDiscountHelper的绑定关系放到之前的依赖项解析器的AddBindings方法当中就大功告成了。
private void AddBindings()
{
kernel.Bind<IValueCalculator>().To<LinqValueCalculator>();
kernel.Bind<IDiscountHelper>().To<DefaultDiscountHelper>;
}
这时我们将这个折扣便应用到了计算总价当中了,这构成了一个依赖项链。 # 指定属性和构造器参数 在将接口和实现进行绑定时,可以为实现类的属性提供一些值。比如,在Discount.cs文件对DefaultDiscountHelper 类添加一个属性:
public class DefaultDiscountHelper : IDiscountHelper
{
public decimal DiscountSize { get; set; }
public decimal ApplyDiscount(decimal totalParam)
{
return (totalParam - (DiscountSize / 100m * totalParam));
}
}
discountSize属性提供不同的折扣值,在对DefaultDiscountHelper和IDiscountHelper接口进行绑定的时候可以给这个属性赋值,修改AddBindings方法如下:
private void AddBindings()
{
kernel.Bind<IValueCalculator>().To<LinqValueCalculator>();
kernel.Bind<IDiscountHelper>().To<DefaultDiscountHelper>().WithPropertyValue("DiscountSize",50M);
}
这样就可以在计算总价时使用50%的折扣了。 当有多个属性时可以链接带哦用WithConstructorArgument方法涵盖所有属性。 当然也可以使用构造器参数来实现参数传递,对DefaultDiscountHelper 类做如下修改:
public class DefaultDiscountHelper : IDiscountHelper
{
public decimal discountSize;
public decimal ApplyDiscount(decimal totalParam)
{
return (totalParam - (discountSize / 100m * totalParam));
}
public DefaultDiscountHelper(decimal discountParma)
{
discountSize = discountParma;
}
}
这样可以在依赖项解析器当中使用WithConstructorArgument方法为构造器参数赋值
private void AddBindings()
{
kernel.Bind<IValueCalculator>().To<LinqValueCalculator>();
kernel.Bind<IDiscountHelper>().To<DefaultDiscountHelper>().WithConstructorArgument("discountParam", 50M);
}
使用条件绑定
在Models文件夹下添加一个FiexibleDiscountHelper类,它实现IDiscount接口,并按照总价给出不同的折扣值。内容如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace EssensialTools.Models
{
public class FlexibleDiscountHelper : IDiscountHelper
{
public decimal ApplyDiscount(decimal totalParam)
{
decimal discount = totalParam > 100 ? 70 : 50;
return (totalParam - (discount / 100m * totalParam));
}
}
}
此时IDiscount接口有两个实现方法,在依赖项解析器中我们可以按照不同条件绑定不同的实现类,在AddBindings方法中作出如下修改:
private void AddBindings()
{
kernel.Bind<IValueCalculator>().To<LinqValueCalculator>();
kernel.Bind<IDiscountHelper>().To<DefaultDiscountHelper>().WithConstructorArgument("discountParam", 50M);
kernel.Bind<IDiscountHelper>().To<FlexibleDiscountHelper>().WhenInjectedInto<LinqValueCalculator>();
}
这样可使当要创建一个LinqValueCalculator对象时使用FlexibleDiscountHelper类作为IDiscountHelper接口的实现类,而不是默认的DefaultDiscountHelper实现类。 Ninject有一些绑定条件的方法:
方法 | 效果 |
---|---|
When(谓词) | 当‘谓词’(一个lambda表达式)的结果为true时,实施绑定 |
WhenClassHas |
当被注入的类以T类型注解属性进行注释时,实施绑定 |
WhenInjectInto |
当要被注入的类时烈性T时,实施绑定 |
设置对象作用域
首先在NinjectDependencyResolver类中引入using Ninject.Web.Common;
命名空间。 此时对AddBindings方法做如下修改:
private void AddBindings()
{
kernel.Bind<IValueCalculator>().To<LinqValueCalculator>().InRequestScope();
...//其他方法未给出
}
绑定的最后使用InRequestScope
方法指定LinqValueCalculator对象的作用域为一个请求,即一个http请求只会创建一个对象而不管有几个依赖项对象。 比如修改Home控制器的初始化器:
public HomeController(IValueCalculator calcParam, IValueCalculator calc2)
{
calc = calcParam;
}
它虽然请求两个IValueCalculator 对象,但MVC只会创建一个IValueCalculator 的实现类LinqValueCalculator的对象。
以下是指定其他作用域的方法: -摘自《精通ASP.NET MVC5》(Adam FreeMan) 名称|效果 -|- InTransientScope()|与未指定作用域相同,为每一个被解析的依赖项创建一个新的对象(每依赖项一实例) InSingeltonScope()
ToConstant(object)|创建一个单一实例,使其应用于整个应用程序(每应用一实例) InThreadScope()|创建一个单一实例,将其用于解析一个线程中各个对象的实例(每线程一实例) InRequestScope()|创建一个单一实例,用于解析一个HTTP请求的各个对象的依赖项(每请求以实例)