1.为什么要用Ninject?

Ninject是一个IOC容器用来解决程序中组件的耦合问题,它的目的在于做到最少配置。其他的的IOC工具过于依赖配置文件,需要使用assembly-qualified名称来进行定义,庸长且复杂常常因为打错字而破坏程序。这些是他的优点,也是为什么要选择它。Ninject同时不能进行热插拔。

2.Ninject做些什么?

其实Ninject做的事情很简单,说白了就是为我们选择一个想要的类来处理事务。来看下面的简单的例子。

    public class Product
{
public int ProductID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public decimal Price { get; set; }
public string Category { get; set; }
} public interface IValueCalculater
{
decimal ValueProducts(params Product[] products);
} public class LinqValueCalculator : IValueCalculater
{
public decimal ValueProducts(params Product[] products)
{
return products.Sum(p => p.Price);
}
}

我们定义了一个实体类,一个接口,一个实现接口的类,这个类完成的功能是计算总价。

  public class ShoppingCart
{
protected IValueCalculater calculator;
protected Product[] products; public ShoppingCart(IValueCalculater calcuParam)
{
this.calculator = calcuParam;
products = new[]{
new Product(){Name="Kayak" , Price=275M},
new Product(){Name="Lifejacket" , Price=48.95M},
new Product(){Name="Scooceer ball" , Price=19.5M},
new Product(){Name="Stadium" , Price=79550M}
};
} public virtual decimal CalculatStockValue()
{
decimal totalPrice = calculator.ValueProducts(products);
return totalPrice;
}
}

ShopingCart类的构造函数使用接口IValueCalculater实现作为一个准备DI的参数。CalculatStockValue方法创建一个Product对象数组,然后调用IValueCalculater接口中的ValueProducts来获得总价。这里可以看到ShoppingCart类中只出现了接口IValueCalculater,却没有出现这个接口的实现类LinqValueCalculator,说的直白点就是ShoppingCart中计算的总价是一个影藏的类实现的,我们可以修改这个影藏的类达到修改总价的目的,而不需要修改ShoppingCart类中的代码。好的,Ninject做的工作就是和上面的类似,是的,它只能完成这么个工作,不过会有很多的变化,最终目的只有一个,就是决定到当前到底要使用哪一个类似LinqValueCalculator着那个的实现类。

3.Ninject的使用

关于如何下载安装Ninject这里不再说了,网上有很多的资源。这里只说几个简单的例子。

            IKernel ninjectKernel = new StandardKernel();
ninjectKernel.Bind<IValueCalculater>().To<LinqValueCalculator>();

上面的代码将想使用的类型和他的接口进行绑定,高祖Ninject,当接收到一个实现IValueCalculater的请求的时候,创建病返回LinqValueCalculator这个类,上面的两个关键字Bind,To可以帮助我们理解他的意思。

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

当我们调用上面两句的时候就会去计算总价。

下面我们看看他的变形。

1.依赖性链

当使用ninjectKernel.Get创建一个类型的时候会检查这个类型与其他类型之间的耦合,如果有额外的依赖性,Ninject会解析这些依赖性,并创建所需要的所有的类的类型。下面我们在LinqValueCalculator中再次依赖其他的类型。

    public interface IDiscountHelper
{
decimal ApplyDiscount(decimal totalParam);
} public class DefalutDiscountHelper : IDiscountHelper
{
private decimal discountRate; public DefalutDiscountHelper(decimal discountParam)
{
discountRate = discountParam;
} public decimal ApplyDiscount(decimal totalParam)
{
return (totalParam - (discountRate / 100M * totalParam));
}
} public class LinqValueCalculator : IValueCalculater
{
IDiscountHelper discounter; public LinqValueCalculator(IDiscountHelper discountParam)
{
discounter = discountParam;
} public decimal ValueProducts(params Product[] products)
{
return discounter.ApplyDiscount(products.Sum(p => p.Price));
}
}

和IValueCalculator所做的那样我们把IDiscountHelper和DefalutDiscountHelper 关联起来。

ninjectKernel.Bind<IDiscountHelper>().To<DefalutDiscountHelper>();
IValueCalculater calcImpl = ninjectKernel.Get<IValueCalculater>();

当IValueCalculater被请求的时候,Ninject知道它要实例化的是LinqValueCalculator,然后进一步考察这个类,并发现他依赖一个可以解析的接口,Ninject会创建DefaultDiscountHelper的一个实例,并把它注入到LinqValueCalculator类的构造器中,一IValueCalculator作为返回结果,不管这个依赖性链有多长,多复杂,Ninject都会以这种方式检查它要实例化的每一个依赖性。

2.指定属性和参数值

修改DefalutDiscountHelper 类如下

    public class DefalutDiscountHelper : IDiscountHelper
{
private decimal discountRate; public decimal DiscountSize { get; set; } public decimal ApplyDiscount(decimal totalParam)
{
return (totalParam - (DiscountSize / 100M * totalParam));
}
}

在使用Ninject将具体类绑定到类型的时候,我已使用WithPropertyValue方法来设置DefalutDiscountHelper 类中的属性DiscountSize,方法如下

            ninjectKernel.Bind<IDiscountHelper>().To<DefalutDiscountHelper>()
.WithPropertyValue("DiscountSize", 50M);
IValueCalculater calcImpl = ninjectKernel.Get<IValueCalculater>();

3.指定构造函数的参数值

如果具体类中有带参数的构造函数可以使用WithConstructorArgument方法来指定这个参数,修改DefalutDiscountHelper如下

    public class DefalutDiscountHelper : IDiscountHelper
{
private decimal discountRate; public decimal DiscountSize { get; set; } public DefalutDiscountHelper(decimal discountParam)
{
discountRate = discountParam;
} public decimal ApplyDiscount(decimal totalParam)
{
return (totalParam - (discountRate / 100M * totalParam));
}
}

使用Ninject绑定如下:

            ninjectKernel.Bind<IDiscountHelper>().To<DefalutDiscountHelper>()
.WithConstructorArgument("discountParam", 50M);
IValueCalculater calcImpl = ninjectKernel.Get<IValueCalculater>();

4.上面我们都是绑定接口,其实只要是有继承关系的两个类之间也可以进行绑定,下面我们来看一个例子,我们先顶一个ShoppingCart类,然后定义一个LimitShoppingCart类来继承它,代码如下:

    public class ShoppingCart
{
protected IValueCalculater calculator;
protected Product[] products; public ShoppingCart(IValueCalculater calcuParam)
{
this.calculator = calcuParam;
products = new[]{
new Product(){Name="Kayak" , Price=275M},
new Product(){Name="Lifejacket" , Price=48.95M},
new Product(){Name="Scooceer ball" , Price=19.5M},
new Product(){Name="Stadium" , Price=79550M}
};
} public virtual decimal CalculatStockValue()
{
decimal totalPrice = calculator.ValueProducts(products);
return totalPrice;
}
}
public class LimitShoppingCart : ShoppingCart
{
public decimal ItemLimit { get; set; } public LimitShoppingCart(IValueCalculater calcParm)
: base(calcParm)
{ } public override decimal CalculatStockValue()
{
var filteredProducts = products.Where(e => e.Price < ItemLimit);
return calculator.ValueProducts(filteredProducts.ToArray());
}
}

下面的代码将子类绑定到它的父类上,代码如下:

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

5.使用条件绑定

可以使用Ninject绑定同一个接口的多个实现,或同一个类的多个派生类,并在不同条件下绑定不同的类。我们可以对当前绑定的具体类进行判断,最终绑定另外一个具体类,先来定义一个IValueCalculator的另外一个实现类,代码如下

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

下面的代码将有选择的创建Ninject类。

ninjectKernel.Bind<IValueCalculater>().To<IterativeValueCalculatgor>().WhenInjectedInto<LimitShoppingCart>();

这段代码的意思是当LimitShoppingCart这个类被绑定的时候才将IterativeValueCalculatgor绑定到它的接口中,我们可以将IterativeValueCalculatgor和LimitShoppingCart看做是一套具体的逻辑,是有具体关系的,例如这是同一次促销的产品,即他们是为同一此促销活动而新建的类,这就为我们的业务逻辑实现提供一个便利。

3.将Ninject用于ASP.NET MVC

这部分本人还没有用到过,是从书本中看到的,也是初次接触。DefaultControllerFactory类是创建控制器类实例的一个类。先看代码吧。

    public class NinjectControllerFactory : DefaultControllerFactory
{
private IKernel ninjectKernel; public NinjectControllerFactory()
{
ninjectKernel = new StandardKernel();
AddBindings();
} private void AddBindings()
{
ninjectKernel.Bind<IValueCalculater>().To<LinqValueCalculator>();
} protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
{
return controllerType == null ? null : (IController)ninjectKernel.Get(controllerType);
}
}

这个类创建了一个Ninject内核,用它对控制器类的请求进行服务,请求是通过GetControllerInstance方法实现的,它是在MVC框架在需要一个控制器对象时调用的。我们不需要明确的绑定控制器,可以依靠默认的自身绑定特性,因为控制器是从System.Web.Mvc.Controller派生来的具体类。

AddBinding方法允许我们队存储库和希望保持送耦合的组件添加Ninject绑定,也可以吧这个方法用于对需要额外的构造器参数或者属性的控制器进行绑定,说的明白点就是我们上面展示的那些需要自己绑定的类。

创建了这个类可以用MVC框架对它进行注册,在Global.asax类的Application_Start方法中来完成注册,代码如下

        protected void Application_Start()
{
AreaRegistration.RegisterAllAreas(); RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes); ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory());
}

现在我们使用NinjectControllerFactory来获得控制器的实例,而Ninject将自动第吧DI运用到控制器对象中。

希望这篇简单的介绍对你有用。

作者:Tyler Ning 
出处:http://www.cnblogs.com/tylerdonet/ 
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,如有问题,可以通过以下邮箱地址williamningdong@gmail.com 联系我,非常感谢。

 
 

使用Ninject来解决程序中组件的耦合问题的更多相关文章

  1. vue程序中组件间的传值方式

    vue程序在组件中进行传值有多种方式,这里记录我在项目中使用到的三种: 1. 父组件向子组件传值 2. 子组件向父组件传值 3. 通过路由传参 父组件通过props向子组件传值 在子组件script中 ...

  2. 小程序中组件公用属性和data-的使用

    属性名                类型                   描述                              注解 hidden                Boo ...

  3. 关于微信小程序中组件和页面对全局样式的继承性

    1.组件只能继承全局样式中的font和color(backgroundcolor不继承) 2.页面可以继承全局样式中所有样式

  4. uni-app-小程序中组件不支持id选择器等

    这个问题刚开始遇到的时候有点搞笑啰,正常情况下,id选择器不是最正常的吗?搞锤子哦. 接着我就找度娘了  ,官网给出的解释是:https://developers.weixin.qq.com/mini ...

  5. 解决element-ui中组件【el-upload】一次性上传多张图片的问题

    element-ui 中的组件 el-upload默认的行为是一张图片请求一次,在项目需求中,通常是多张图片要求只向后台请求一次,下面的做法就是为了实现这样的需求 前端 <el-upload r ...

  6. android 程序中res/values-v14/styles.xml报错的解决办法

    从旧的ADT迁移的新的ADT时, android 程序中res/values-v14/styles.xml报错: error: Error retrieving parent for item: No ...

  7. OD提示 "为了执行系统不支持的动作, OllyICE 在这个被调试的程序中注入了一点代码, 但是经过5秒仍未收到响应..." 解决办法

    别的OD就可以,我自己整合过的一个很顺手的OD就是不行,最后找到了解决办法: 转自:http://bbs.pediy.com/showthread.PHP?t=97629 -------------- ...

  8. 微信小程序image组件binderror使用例子(对应html、js中的onerror)

    官方文档  binderror HandleEvent 当错误发生时,发布到 AppService 的事件名,事件对象event.detail = {errMsg: 'something wrong' ...

  9. 理解性能的奥秘——应用程序中慢,SSMS中快(4)——收集解决参数嗅探问题的信息

    本文属于<理解性能的奥秘--应用程序中慢,SSMS中快>系列 接上文:理解性能的奥秘--应用程序中慢,SSMS中快(3)--不总是参数嗅探的错 前面已经提到过关于存储过程在SSMS中运行很 ...

随机推荐

  1. hdu 1053 Entropy

    题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=1053 Entropy Description An entropy encoder is a data ...

  2. angularjs+nodejs+mongodb三件套

    说实话,自己对于web前段的认识并不是太深入,但是因为项目的需要,所以有的时候肯定会需要接触到web前段的知识点.说到web前端想必大家肯定会想到css+js+html,的确web前端的工作,从某总角 ...

  3. virtualbox下 ubuntu 14.04设置外网独立IP

    安装时记得选择sshserver vim /etc/network/interfaces iface eth0 inet static address YOUR IP netmask 子网掩码 get ...

  4. 触摸屏校准tslib的配置文件

    ./autogen.sh#sleep 10./configure --prefix=/usr/lxl/tslib --host=arm-linux CC=arm-linux-gcc#sleep 100 ...

  5. 5个SQL核心

    5个核心的SQL语句 1.SELECT -查询语句的逻辑处理顺序 5     SELECT <columnlist> 1     FROM <source objectlist> ...

  6. [shell基础]——read命令

    read命令:在shell中主要用于读取输入.变量.文本 1. 接受标准输入(键盘)的输入,并将输入的数据赋值给设置的变量      [按回车键——表示输入完毕]      [若输入的数据多于设置的变 ...

  7. [转载]poi导出excel,可以自定义保存路径

    poi导出excel比js导出excel安全性更好,在使用poi导出excel时,先要导入poi-3.5-FINAL-20090928.jar包到你项目的lib目录下,我这里选择是3.5版的 1.ac ...

  8. 11.2Daily Scrum

    人员 任务分配完成情况 明天任务分配 王皓南 做文件的网页和数据库连接,任务编号771 实现视频上传的功能 申开亮 实现视频浏览的功能,任务编号772 实现视频浏览的功能 王宇杰 后台测试,任务编号7 ...

  9. Eclipse中的常用快捷键

    快捷修复 Command+1 //int a=100L; //int a=(int) 100L; 快捷删除行 Command+D 快速起新行 Shift+Enter (当本行代码很长时,将光标定在本行 ...

  10. Linux 下开放指定端口

    安装tomcat后,在客户端输入地址  http://localhost:8080/ ,发现默认端口8080不能访问. 由于Linux防火墙默认是关闭8080端口.因此,若要能够访问8080端口,可以 ...