前面也有说"控制反转"所谓的依赖注入(Dependency Injection)简称DI。针对它有一款不错的容器,那就是"Ninject",这东西是一个简单易用的东西。话不多说,直接开始吧!
使用Ninject
先用控制台程序玩玩首先定义一个类,接口以及它的实现,代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Ninject_Tools { public class Product : Object { 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; } } }
下面创建一个接口,具体代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Ninject_Tools { public interface IValueCalculator { decimal ValueProducts(params Product[] products); } }
注:上面的IValueCalculator里面多了一个ValueProducts的方法,用于返回商品的累计值。
接着我们写一个类来实现接口IValueCalculator,代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Ninject_Tools { public class LinqValueCalculator : IValueCalculator { public decimal ValueProducts(params Product[] products) { return products.Sum(h => h.Price); } } }
注:这里用LINQ的扩张方法来计算Prducts商品的总价,当然还有其他的办法也可以实现。
接下来我们需要创建一个类来实现依赖注入,具体的代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Ninject_Tools { public class ShoppingCart : Object { private IValueCalculator calcuator; public ShoppingCart(IValueCalculator calcParam) { this.calcuator = calcParam; } public decimal CalculateStockValue() { //计算产品集总和 Product[] products = { new Product(){Name="Huitai",Price=1M}, new Product(){Name="ShuaiShuai",Price=10M}, new Product(){Name="Jack",Price=100M}, new Product(){Name="Cena",Price=200M} }; //计算产品总和 decimal totaValue = this.calcuator.ValueProducts(products); //返回结果 return totaValue; } } }
注:ShoppingCart构造器接受接口IValueCalculator的实现的实例作为参数来构造注入来实现ShoppingCart与LinqValueCalculator(IValueCalculator接口实现)的解耦,这也就是依赖注入里的"构造注入(Constructor Injection)".它们之间的关系如下图1.
图1.
由上面的图可以看出ShoppingCart类和LinqValueCalculator类都依赖于IValueCalcutor,ShoppingCart和LinqValueCalculator没有直接关系,甚至都不知道他的存在。
下面我们就需要添加Ninject到我们的项目里来玩玩,在我们的应用程序里使用管理程序"NuGet"包来引入Ninject的DLL,如下图2.给我们的应用程序来加载进来Ninject.dll.
图2.
加载进来Ninject.dll,然后我们就需要开始使用它了。
然后我们在控制台程序的Mian方法里使用它,具体的代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Ninject; namespace Ninject_Tools { class Program { static void Main(string[] args) { //创建Ninject实例,方便我们下面使用 IKernel ninjectKerenl = new StandardKernel(); //绑定相关的类型和创建的接口 ninjectKerenl.Bind<IValueCalculator>().To<LinqValueCalculator>(); //得到接口的实现 IValueCalculator calcImpl = ninjectKerenl.Get<IValueCalculator>(); //创建实例ShoppingCart实例依赖注入 ShoppingCart cart = new ShoppingCart(calcImpl); //输出 Console.WriteLine("Total:{0:C}", cart.CalculateStockValue()); } } }
好了,现在跑一下我们的程序,可以看到如下图3.的结果。
图3.
小结一下吧!我们开始解耦,然后将IValueCalcutor的实例对象作为参数传递给ShoppingCart的构造器的方式,然后我们通过"依赖注入"容器来处理这个参数,在上面我们用Ninject来实现的,到这里相信大家对NinJect有一点初步的认识。
创建依赖链(Creating Chains of Dependency)
当向Ninject创建一个类型,它检查该类型与其它类型之间的耦合关系,类型和其他类型。如果有额外的依赖,Ninject解决它们并创建新的我们需要的类的实例。
给我们的控制台程序继续添加一个新的接口IDiscountHelper进来,代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Ninject_Tools { public interface IDiscountHelper { decimal ApplyDiscount(decimal totalParam); } }
然后在添加一个实现新接口IDiscountHelper的类DefaultDiscountHelper进来,代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Ninject_Tools { public class DefaultDiscountHelper : IDiscountHelper { public decimal ApplyDiscount(decimal totalParam) { return (totalParam - (10m / 100m * totalParam)); } } }
然后在我们上面的LinqValueCalculator类里添加依赖,代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Ninject_Tools { public class LinqValueCalculator : IValueCalculator { private IDiscountHelper discounter; //LinqValueCalculator构造器接受IDiscountHelper实例对象 public LinqValueCalculator(IDiscountHelper discountParam) { this.discounter = discountParam; } public decimal ValueProducts(params Product[] products) { return this.discounter.ApplyDiscount(products.Sum(h => h.Price)); } } }
然后就是绑定实现接口,具体代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Ninject; namespace Ninject_Tools { class Program { static void Main(string[] args) { //创建Ninject实例,方便我们下面使用 IKernel ninjectKerenl = new StandardKernel(); //绑定相关的类型和创建的接口 ninjectKerenl.Bind<IValueCalculator>().To<LinqValueCalculator>(); ninjectKerenl.Bind<IDiscountHelper>().To<DefaultDiscountHelper>(); //得到接口的实现 IValueCalculator calcImpl = ninjectKerenl.Get<IValueCalculator>(); //创建实例ShoppingCart实例依赖注入 ShoppingCart cart = new ShoppingCart(calcImpl); //输出 Console.WriteLine("Total:{0:C}", cart.CalculateStockValue()); } } }
然后在跑下我们修改后的程序,结果如下图4.
图4.
小结一下吧:代码中Ninject将两个接口分别绑定到对应的实现类中,我们没有改变实现IValueCalculator类的代码。当我们请求IValueCalculator的类型时Ninject知道就实例化LinqValueCalculator对象来返回。但是它会去检查这个类,发现LinqValueCalculator还同时依赖另一个接口,并且这个接口是它能解析的,Ninject创建一个DefaultDiscountHelper的实例注入LinqValueCalculator类的构造器,并返回IValueCalculator类型的对象。Ninject会只用这种方式检查每一个要实例化的类的依赖,不管依赖链有多的复杂。
指定属性和参数值(Specifying Property and Parameter Values)
我们可以配置类提供一些属性或者具体的值传给Ninject,当我们绑定和接口的实现。然后我们对上面的DefaultDiscountHelper类写死的折扣经行修改。
然后我们给DefaultDiscountHelper类添加一条属性,代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Ninject_Tools { public class DefaultDiscountHelper : IDiscountHelper { public decimal DiscountSize { get; set; } public decimal ApplyDiscount(decimal totalParam) { return (totalParam - (this.DiscountSize / 100m * totalParam)); } } }
然后在我们绑定实现接口的时候,就可以动态的使用WithPropertyValue来动态的设置我们的折扣力度。代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Ninject; namespace Ninject_Tools { class Program { static void Main(string[] args) { //创建Ninject实例,方便我们下面使用 IKernel ninjectKerenl = new StandardKernel(); //绑定相关的类型和创建的接口 ninjectKerenl.Bind<IValueCalculator>().To<LinqValueCalculator>(); ninjectKerenl.Bind<IDiscountHelper>().To<DefaultDiscountHelper>().WithPropertyValue("DiscountSize",50M); //得到接口的实现 IValueCalculator calcImpl = ninjectKerenl.Get<IValueCalculator>(); //创建实例ShoppingCart实例依赖注入 ShoppingCart cart = new ShoppingCart(calcImpl); //输出 Console.WriteLine("Total:{0:C}", cart.CalculateStockValue()); } } }
添加了属性的项目跑起来看是不是我们输入5折的折扣,运行起来如下图5.
图5.
使用自动绑定(Self-Binding)
一个有用的功能整合到你的代码中完全是Ninject自我绑定,也就是来自Ninject内核被请求的类的地方。这似乎是一个奇怪的事情,但是这意味着我们不用手动的执行DI初始化,空值太程序可以修改如下代码所示:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Ninject; namespace Ninject_Tools { class Program { static void Main(string[] args) { //创建Ninject实例,方便我们下面使用 IKernel ninjectKerenl = new StandardKernel(); //绑定相关的类型和创建的接口 ninjectKerenl.Bind<IValueCalculator>().To<LinqValueCalculator>(); ninjectKerenl.Bind<IDiscountHelper>().To<DefaultDiscountHelper>().WithPropertyValue("DiscountSize", 50M); ////得到接口的实现 //IValueCalculator calcImpl = ninjectKerenl.Get<IValueCalculator>(); ////创建实例ShoppingCart实例依赖注入 //ShoppingCart cart = new ShoppingCart(calcImpl); ////输出 //这里是调用ShoppingCart本身,所以注释掉这行代码程序也可以执行,但是调用具体的类经行自我绑定,代码如下: //ninjectKernel.Bind<ShoppingCart>().ToSelf().WithParameter("<parameterName>", <paramvalue>); ninjectKerenl.Bind<ShoppingCart>().ToSelf(); ShoppingCart cart = ninjectKerenl.Get<ShoppingCart>(); Console.WriteLine("Total:{0:C}", cart.CalculateStockValue()); } } }
运行程序可以看到如下图6.所示的结果
图6.
绑定派生类型(Binding to a Derived Type)
前面一直都在接口绑定的方面(因为接口在MVC里相关性更强),我们也可以使用Ninject绑定具体类。Self-Binding就是这样搞的。那样我们可以使用Ninject绑定一个具体的类到它的派生类,因为前面我们绑定接口到实现它的具体类--实现类继承了该接口,这里就是类继承类罢了。那我们修改一下ShoppingCart类,代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Ninject_Tools { public class ShoppingCart : Object { protected IValueCalculator calcuator; protected Product[] products; public ShoppingCart(IValueCalculator calcParam) { this.calcuator = calcParam; //计算产品集总和 Product[] products = { new Product(){Name="Huitai",Price=1M}, new Product(){Name="ShuaiShuai",Price=10M}, new Product(){Name="Jack",Price=100M}, new Product(){Name="Cena",Price=200M} }; } public virtual decimal CalculateStockValue() { //计算产品总和 decimal totaValue = this.calcuator.ValueProducts(products); //返回结果 return totaValue; } } }
写一个LimitShoppingCart类派生自ShoppingCart类:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Ninject_Tools { public class LimitShoppingCart : ShoppingCart { public LimitShoppingCart(IValueCalculator calcParam) : base(calcParam) { } public override decimal CalculateStockValue() { //过滤掉价格超过我们设定价格的商品然后在求和 var filteredProducts = products.Where(h => h.Price < ItemLimit); return this.calcuator.ValueProducts(filteredProducts.ToArray()); } public decimal ItemLimit { get; set; } } }
然后绑定ShoppingCart到它的派生类LimitShoppingCart类,代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Ninject; namespace Ninject_Tools { class Program { static void Main(string[] args) { //创建Ninject实例,方便我们下面使用 IKernel ninjectKerenl = new StandardKernel(); //绑定相关的类型和创建的接口 ninjectKerenl.Bind<IValueCalculator>().To<LinqValueCalculator>(); ninjectKerenl.Bind<IDiscountHelper>().To<DefaultDiscountHelper>().WithPropertyValue("DiscountSize", 50M); ////得到接口的实现 //IValueCalculator calcImpl = ninjectKerenl.Get<IValueCalculator>(); ////创建实例ShoppingCart实例依赖注入 //ShoppingCart cart = new ShoppingCart(calcImpl); ////输出 //这里是调用ShoppingCart本身,所以注释掉这行代码程序也可以执行,但是调用具体的类经行自我绑定,代码如下: //ninjectKernel.Bind<ShoppingCart>().ToSelf().WithParameter("<parameterName>", <paramvalue>); //ninjectKerenl.Bind<ShoppingCart>().ToSelf(); ninjectKerenl.Bind<ShoppingCart>().To<LimitShoppingCart>().WithPropertyValue("ItemLimit", 100M); ShoppingCart cart = ninjectKerenl.Get<ShoppingCart>(); Console.WriteLine("Total:{0:C}", cart.CalculateStockValue()); } } }
使用条件绑定(Using Conditional Binding)
我们可对同一个接口会有多重实现或者是对同一个类有多个派生,这时Ninject可以指定不同的条件来说明哪一个应该被使用。比如下面,我们创建一个新的类IterativeValueCalculator实现IValueCalculator,代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Ninject_Tools { 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设置条件来指定哪个显示被应用。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Ninject; namespace Ninject_Tools { class Program { static void Main(string[] args) { //创建Ninject实例,方便我们下面使用 IKernel ninjectKerenl = new StandardKernel(); //绑定相关的类型和创建的接口 ninjectKerenl.Bind<IValueCalculator>().To<LinqValueCalculator>(); //在这里绑定可以使用条件绑定 ninjectKerenl.Bind<IValueCalculator>().To<IterativeValueCalculator>().WhenInjectedInto<ShoppingCart>(); //ninjectKerenl.Bind<IDiscountHelper>().To<DefaultDiscountHelper>().WithPropertyValue("DiscountSize", 50M); ////得到接口的实现 //IValueCalculator calcImpl = ninjectKerenl.Get<IValueCalculator>(); ////创建实例ShoppingCart实例依赖注入 //ShoppingCart cart = new ShoppingCart(calcImpl); ////输出 //这里是调用ShoppingCart本身,所以注释掉这行代码程序也可以执行,但是调用具体的类经行自我绑定,代码如下: //ninjectKernel.Bind<ShoppingCart>().ToSelf().WithParameter("<parameterName>", <paramvalue>); //ninjectKerenl.Bind<ShoppingCart>().ToSelf(); ninjectKerenl.Bind<ShoppingCart>().To<LimitShoppingCart>().WithPropertyValue("ItemLimit", 1000M); ShoppingCart cart = ninjectKerenl.Get<ShoppingCart>(); Console.WriteLine("Total:{0:C}", cart.CalculateStockValue()); } } }
从上面的代码可以看出,绑定指定IterativeValueCalculator类应该被实例化在绑定IValueCalculatorinterface时被当对象的依赖注入。如果我们的条件没有一个合适也没有关系,Ninject会寻找一个对该类或接口的默认的绑定,以至于Ninject会有一个返回的结果。
在Asp.Net MVC 中使用Ninject及运用单元测试
前面是都是控制台程序里面玩Ninject,在MVC玩Ninject可就没那么简单了,比起来就稍微有点复杂了。开始是我们需要创建一个类,它的派生自System.Web.Mvc.DefaultControllerFactory。该类MVC依赖默认情况下创建控制器类的实例。.Net有好多单元测试包,其中又由好多包开放源代码,但愿测试工具最受欢迎那可能就是NUnit。我们就玩玩怎么创建但愿测试和填充测试。
首先先看看在Asp.Net MVC 中如何使用Ninject,开始需要创建一个类,让他继承DefaultControllerFactory ,代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using Ninject; using System.Web.Routing; namespace MVC_Tools.Models { public class NinjectControllerFactory : DefaultControllerFactory { private IKernel ninjectKernel; public NinjectControllerFactory() { this.ninjectKernel = new StandardKernel(); AddBindings(); } protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType) { return controllerType == null ? null : (IController)ninjectKernel.Get(controllerType); } private void AddBindings() { //把额外的东西绑定到这里 this.ninjectKernel.Bind<IProductRepository>().To<FakeProductRepository>(); } } }
这个类创建一个Ninject内核,通过GetControllerInstance方法为控制器类的请求服务,这个方法在需要一个控制器对象的时候被MVC框架调用。我们不需要显式地使用Ninject绑定控制器类。我们可以依靠默认的self-binding(自我绑定)特性,因为controllersare具体类派生自 System.Web.Mvc.Controller。AddBindings()方法允许我们绑定Repositories和其他需要保持松耦合的组件。我们也可以使用这个方法绑定需要额外的构造器参数或属性参数的controller classes。
一旦我们创建这个类,我们必须注册它与MVC框架,我们需要在Global.asax文件测试它,具体代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.Web.Routing; using MVC_Tools.Models; namespace MVC_Tools { // 注意: 有关启用 IIS6 或 IIS7 经典模式的说明, // 请访问 http://go.microsoft.com/?LinkId=9394801 public class MvcApplication : System.Web.HttpApplication { public static void RegisterGlobalFilters(GlobalFilterCollection filters) { filters.Add(new HandleErrorAttribute()); } public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( "Default", // 路由名称 "{controller}/{action}/{id}", // 带有参数的 URL new { controller = "Home", action = "Index", id = UrlParameter.Optional } // 参数默认值 ); } protected void Application_Start() { AreaRegistration.RegisterAllAreas(); RegisterGlobalFilters(GlobalFilters.Filters); RegisterRoutes(RouteTable.Routes); ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory()); } } }
现在MVC框架将使用我们的NinjectControllerFactoryto获得实例的控制器类,Ninject会处理迪向控制器对象自动。有关这东西就先了解到这里吧!
下面看我们的单元测试,弄一个控制台程序来搞吧!
创建相关的Product类,IPriceReducer,IProductRepository接口如下面:
IProductRepository定义了一个存储库,我们通过将获取和更新Product对象。IPriceReducer定义了一个具体的降价方法,针对所有的Products。我们的目标就是实现这些接口并需要遵循下面的情况:
- 所有的Product都应该降价
- 总共的价格必须等于所有的Product数量乘以降价数额的乘积
- 降价后的Products必须大于1美元
- Repository的UpdateProduct方法应该被每一个Product商品调用到
然后我们添加一个实现接口IProductRepository的类FakeRepository,代码如下:
还需要添加一个类MyPriceReducer去实现IPriceReducer接口,代码如下:
开始我们的单元测试吧!我们要按照TDD模式和编写单元测试代码之前编写应用程序,点击MyPriceReducer类里的ReducePrices方法,然后选择"创建单元测试",如下图6.
图6.当选择"创建单元测试"会出现如下图7.所示的弹出框,
图7.由于单元测试是一个单独的项目,所以我们给他取名"Test_Project",,创建完成后会新建,一个单元测试的项目加载进来,并生成了一个测试类MyPriceReducerTest,它里面包含一些属性和方法让供我们使用,具体生成代码如下:
但是对我们来说最重要莫过于项目了,所以我们将代码修改成下面的形式:
运行单元测试的的代码,结果如下图8.所示
图8.因为我们没有去实现ReducePrices,单元测试不能通过。有很多不同的方式来构建单元测试年代。常见的是有一个方法是测试所需的所有功能的条件。但是我们更喜欢创建很多小的单元测试,每个单元测试只关注应用程序的一个方面。当让给单元测试方法名字的命名规则根据自己的编程风格自己定义,只要符合命名规范即可。把测试的全部功能分散到一个一个的小的测试方法里经行,通过这种方式不断的完善我们的驱动测试开发(TDD).如下面的代码:
然后运行我们的单元测试代码,结果如图9.
图9.其实上面的单元的测试代码里也使用了简单依赖注入(构造器注入),上面的每一个测试方法就是针对一个功能来测试,貌似测试没有通过,那是因为我们的ReducePrices方法没有实现,现在我们就是去搞下他吧!代码具体如下:
搞完之后,在来跑下我们的单元测试的模块,运行结果如下图10.
图10.呵呵!其实这些基本都是VS里面的东西,差不多随便看看点点,写写就会明白的吧!好了就先分享这么一点东西吧!大家共同学习进步,文章里那里要是有错误还是那里描述错误的,请路过的朋友们,前辈们多多指导,批评。这样我们才能更好的学习。
前面几话都讲的一些有关MVC相关东西,从这话开始应用实战的项目开始。
实战一个简单的购物流程的项目吧!
首先创建一个空白的解决方案,如下图1.
图1.我们预计创建3个模块,一个模块包含我们的域模型(DoMain),一个模块包含我的MVC Web应用程序,还有一个单元测试的模块。
我们的域模型(DoMain)是一个类库项目,然后是一个Asp.Net MVC3 的Web应用程序(Razor引擎)项目,然后添加一个测试项目进来,添加测试项目如下图2.
图2.当我们创建好我们的域模型(DoMain)类库项目和测试项目(类库项目),VS会自动创建一个Class1.cs的文件和UnitTest1.cs的文件,这个对我们来说没有多大的用处,可以直接干掉。之后我们的行么如下图3.
图3.下一步就是添加项目引用,可项目需要用到包(扩展工具/第三方插件),我们项目具体要用到的第三方插件如下:
具体项目 | 第三插件名称 |
SportsStore.Domain | Ninject |
SportsStore.UI | Ninject |
SportsStore.Domain | Moq |
SportsStore.UI | Moq |
可以在VS里面的"程序包控制管理"用下面的命令导入第三方插件包,命令如下:
Install-Package Ninject -Project SportsStore.WebUI
Install-Package Ninject -Project SportsStore.Domain
Install-Package Moq -Project SportsStore.WebUI
Install-Package Moq -Project SportsStore.Domain
也可以在相关项目上右键,使用NuGet程序包管理一个一个导入,方法根据自己所好,不在啰嗦!
然后就是我们项目的依赖关系,如下表所示:
具体项目 | 依赖项目 |
SportsStore.Domain | 无 |
SportsStore.UI | SportsStore.Domain |
SportsStore.UnitTests |
SportsStore.Domain SportsStore.UI |
因为我们将使用Ninject创建我们的MVC应用程序控制器和处理DI,所以我们需要创建一个新的类更改配置。在SportsStore.UI应用程序里创建一个文件夹(命名"Infrastructure")然后在改文件里创建一个类叫NinjectControllerFactory,它的代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using Ninject; using System.Web.Routing; using Moq; using SportsStore.Domain.Abstract; using SportsStore.Domain.Entities; namespace SportsStore.WebUI.Infrastructure { public class NinjectControllerFactory : DefaultControllerFactory { private IKernel ninjectKernel; public NinjectControllerFactory() { this.ninjectKernel = new StandardKernel(); AddBindings(); } protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType) { return controllerType == null ? null : (IController)ninjectKernel.Get(controllerType); } private void AddBindings() { //绑定额外数据 } } }
我们需要注册NinjectControllerFactory到MVC框架,所以我们也需要在Global.asax.cs里给它注册进去,具体代码如下:
protected void Application_Start() { AreaRegistration.RegisterAllAreas(); RegisterGlobalFilters(GlobalFilters.Filters); RegisterRoutes(RouteTable.Routes); //注册路由 ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory()); }
我们可以试着启动跑下我的MVC Web项目,结果如下图4.
图4.如果真出现这个错误页面也是预计必然的结果,接下来的任务就是让这个页面消失吧!
从我们的域模型(DoMain)开始吧!既然我们搞的是一个购物流程的项目,那我们肯定需要商品才能购物,那就在域模型(Domain)里创建一个文件夹(命名"Entities")放相应的模型在该文件夹来创建一个Product类吧!Product类的代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace SportsStore.Domain.Entities { public class Product : Object { 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; } } }
接下来创建一个抽象存储库,我们知道我们用一些方法可以是Prodcut和数据交互,这里我们使用存储库模式,我们不需要担心他是如何去实现,所以在域模型(DoMain)项目里建立一个文件夹(命名"Abstract")在该文件里创建一个接口"IProductRepository",它的代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using SportsStore.Domain.Entities; namespace SportsStore.Domain.Abstract { public interface IProductRepository { IQueryable<Product> Products { get; } } }
这个接口使用这个IQueryable < T >可以获取Product对象,它没有说任何关于如何或数据存储在哪里或者它将如何被检索。一个类,它使用IProductRepository接口就可以获得Product对象,但是不需要知道任何关于它们来自于哪儿,或者他们如何将被交付到那儿,这是最基本的存储库的模式。
然后我们使用模拟库,因为我们一定定义了一个接口那么我接着就实现它,让他跟数据交互,我们模拟一下实现IProductRepository接口,代码如下:
private void AddBindings() { //绑定额外数据 //模拟IProductRepository实现 Mock<IProductRepository> mock = new Mock<IProductRepository>(); mock.Setup(h => h.Products).Returns(new List<Product>{ new Product {Name="FootBall",Price=25}, new Product {Name="Surf Board",Price=179}, new Product {Name="Running shoes",Price=95} }.AsQueryable()); this.ninjectKernel.Bind<IProductRepository>().ToConstant(mock.Object); }
准备工作的差不多了我们需要要能展现的东西出来才不算前功尽弃,我们要展示出我们的商品,首先要来创建相应的控制器(命名"ProductController"),代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using SportsStore.Domain.Abstract; namespace SportsStore.WebUI.Controllers { public class ProductController : Controller { private IProductRepository repository; public ProductController(IProductRepository productReposittory) { this.repository = productReposittory; } } }
这个只不过是一个的空的控制器,我们创建了一个构造函数,该函数接收IProductRepository来的参数,这里也就方便Ninject在Product对象实例化的时候的注入(构造注入)。然后我需要返回一个视图展示出来,所以修改ProductController控制器如下:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using SportsStore.Domain.Abstract; namespace SportsStore.WebUI.Controllers { public class ProductController : Controller { private IProductRepository repository; public ProductController(IProductRepository productReposittory) { this.repository = productReposittory; } //返回一个视图 public ViewResult List() { return this.View(this.repository.Products); } } }
接下来,需要添加一个视图(View),我们需要创建一个强类型视图,如下图5.
图5.当然在我们选择模型类的时候,下拉框并不能找到IEnumerable<SportsStore.Domain.Entities.Product>,因为他不会包含枚举的域模型(DoMain)对象,所以需要我们手动输入。
IEnumerable<Product>意味着我们可以创建一个列表,现在就用犀利Razor引擎来搞这个页面,List.cshtml页面代码如下:
@model IEnumerable<SportsStore.Domain.Entities.Product> @{ ViewBag.Title = "Product List"; } @foreach (var Product in Model) { <div class="item"> <h3>@Product.Name</h3> @Product.Description <h4>@Product.Price.ToString("C")</h4> </div> }
说明:@Product.Price.ToString("C"),ToString("C")根据你的服务器将数字转换为相应的货币。
然后我们需要修改一下默认的路由,具体的修改如下:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.Web.Routing; using SportsStore.WebUI.Infrastructure; namespace SportsStore.WebUI { // 注意: 有关启用 IIS6 或 IIS7 经典模式的说明, // 请访问 http://go.microsoft.com/?LinkId=9394801 public class MvcApplication : System.Web.HttpApplication { public static void RegisterGlobalFilters(GlobalFilterCollection filters) { filters.Add(new HandleErrorAttribute()); } public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( "Default", // 路由名称 "{controller}/{action}/{id}", // 带有参数的 URL new { controller = "Product", action = "List", id = UrlParameter.Optional } // 参数默认值 ); } protected void Application_Start() { AreaRegistration.RegisterAllAreas(); RegisterGlobalFilters(GlobalFilters.Filters); RegisterRoutes(RouteTable.Routes); //注册路由 ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory()); } } }
注明:修改就是上面代码红色部分里标识为蓝色的控制器名称和相应方法(Action)名称。
接下来,在跑下我们的MVC Web应用程序,运行结果如下图6所示.
图6.可以看到我们已经消灭之前的黄页了,项目开始就先搞怎么些东西,后续继续完善。要是那里描述有误还请路过的前辈牛人给点指点,这样才能更好的进步,谢谢!
ASP.NET MVC
posted @ 2012-07-04 18:00 辉太 阅读(554) | 评论 (1) 编辑 |
posted @ 2012-06-30 09:46 辉太 阅读(175) | 评论 (0) 编辑 |
posted @ 2012-06-26 12:36 辉太 阅读(231) | 评论 (0) 编辑 |
posted @ 2012-06-20 18:34 辉太 阅读(1226) | 评论 (10) 编辑 |
posted @ 2012-06-19 17:33 辉太 阅读(1307) | 评论 (9) 编辑 |