zoukankan      html  css  js  c++  java
  • Asp.Net MVC 3.0

    前面也有说"控制反转"所谓的依赖注入(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,代码如下:

    View Code
    复制代码
    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设置条件来指定哪个显示被应用。

    View Code
    复制代码
    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 ,代码如下:

    View Code
    复制代码
    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接口如下面:

    View Code
    View Code
    View Code

    IProductRepository定义了一个存储库,我们通过将获取和更新Product对象。IPriceReducer定义了一个具体的降价方法,针对所有的Products。我们的目标就是实现这些接口并需要遵循下面的情况:

    • 所有的Product都应该降价
    • 总共的价格必须等于所有的Product数量乘以降价数额的乘积
    • 降价后的Products必须大于1美元
    • Repository的UpdateProduct方法应该被每一个Product商品调用到

    然后我们添加一个实现接口IProductRepository的类FakeRepository,代码如下:

    View Code

    还需要添加一个类MyPriceReducer去实现IPriceReducer接口,代码如下:

    View Code

    开始我们的单元测试吧!我们要按照TDD模式和编写单元测试代码之前编写应用程序,点击MyPriceReducer类里的ReducePrices方法,然后选择"创建单元测试",如下图6.

    图6.当选择"创建单元测试"会出现如下图7.所示的弹出框,

    图7.由于单元测试是一个单独的项目,所以我们给他取名"Test_Project",,创建完成后会新建,一个单元测试的项目加载进来,并生成了一个测试类MyPriceReducerTest,它里面包含一些属性和方法让供我们使用,具体生成代码如下:

    View Code

     但是对我们来说最重要莫过于项目了,所以我们将代码修改成下面的形式:

    View Code

    运行单元测试的的代码,结果如下图8.所示

    图8.因为我们没有去实现ReducePrices,单元测试不能通过。有很多不同的方式来构建单元测试年代。常见的是有一个方法是测试所需的所有功能的条件。但是我们更喜欢创建很多小的单元测试,每个单元测试只关注应用程序的一个方面。当让给单元测试方法名字的命名规则根据自己的编程风格自己定义,只要符合命名规范即可。把测试的全部功能分散到一个一个的小的测试方法里经行,通过这种方式不断的完善我们的驱动测试开发(TDD).如下面的代码:

    View Code

    然后运行我们的单元测试代码,结果如图9.

    图9.其实上面的单元的测试代码里也使用了简单依赖注入(构造器注入),上面的每一个测试方法就是针对一个功能来测试,貌似测试没有通过,那是因为我们的ReducePrices方法没有实现,现在我们就是去搞下他吧!代码具体如下:

    View Code

    搞完之后,在来跑下我们的单元测试的模块,运行结果如下图10.

    图10.呵呵!其实这些基本都是VS里面的东西,差不多随便看看点点,写写就会明白的吧!好了就先分享这么一点东西吧!大家共同学习进步,文章里那里要是有错误还是那里描述错误的,请路过的朋友们,前辈们多多指导,批评。这样我们才能更好的学习。

     
    分类: ASP.NET MVC

    前面几话都讲的一些有关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

    ASP.NET MVC

    摘要: 前面几话都讲的一些有关MVC相关东西,从这话开始应用实战的项目开始。实战一个简单的购物流程的项目吧!首先创建一个空白的解决方案,如下图1.图1.我们预计创建3个模块,一个模块包含我们的域模型(DoMain),一个模块包含我的MVC Web应用程序,还有一个单元测试的模块。我们的域模型(DoMain)是一个类库项目,然后是一个Asp.Net MVC3 的Web应用程序(Razor引擎)项目,然后添加一个测试项目进来,添加测试项目如下图2.图2.当我们创建好我们的域模型(DoMain)类库项目和测试项目(类库项目),VS会自动创建一个Class1.cs的文件和UnitTest1.cs的文件,这个对阅读全文

    posted @ 2012-07-04 18:00 辉太 阅读(554) | 评论 (1) 编辑 |

    摘要: 前面也有说"控制反转"所谓的依赖注入(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阅读全文

    posted @ 2012-06-30 09:46 辉太 阅读(175) | 评论 (0) 编辑 |

    摘要: 这一次在此讲述MVC模式,让大家对MVC有一个更加深刻的影响,为大家的深入学习做好坚定的基础!如果对MVC模概念还是混淆的新同学,这话一定要好好学习了!理解MVC模式 MVC模式意味着MVC应用程序将被分成至少三个部件:Models(模型):用于封装与应用程序的业务逻辑相关的数据以及对数据的处理方法。“模型”有对数据直接访问的权力,例如对数据库的访问。“模型”不依赖“视图”和“控制器”,也就是说,模型不关心它会被如何显示或是如何被操作。但是模型中数据的变化一般会通过一种刷新机制被公布。为了实现这种机制,那些用于监视此模型的视图必须事先在此模型上注册,从而,视图可以了解在数据模型上发生的改变。V阅读全文

    posted @ 2012-06-26 12:36 辉太 阅读(231) | 评论 (0) 编辑 |

    摘要: 第一话简单介绍了一下Asp.Net MVC,接下来就慢慢进门看呗!首先动态输入还是接着昨天的项目修改着玩吧!修改一天昨天的HomeController吧!(为了方便这次把注释卸载代码里,新同学们注意了)。using System;using System.Collections.Generic;using System.Linq;using System.Web;using System.Web.Mvc;namespace Hello_Asp.Net_Mvc.Controllers{ public class HomeController : Controller { ...阅读全文

    posted @ 2012-06-20 18:34 辉太 阅读(1226) | 评论 (10) 编辑 |

    摘要: 在用Asp.Net MVC写这个Hello World之前,先来聊一下这个MVC到底是什么东西!MVC是一种模式,是一种软件的构架模式。它把软件系统分为三个部分:模型(Model),视图(View)和控制器(Controller)。MVC模式的目的是实现一种动态的程序设计,使后续对程序的修改和扩展简化,并且使程序某一部分的重复利用成为可能。除此之外,此模式通过对复杂度的简化,使程序结构更加直观。软件系统通过对自身基本部份分离的同时也赋予了各个基本部分应有的功能。模型(Model),视图(View)和控制器(Controller)简称"MVC",它们之间的关系如下图1.图1.阅读全文

    posted @ 2012-06-19 17:33 辉太 阅读(1307) | 评论 (9) 编辑 |

  • 相关阅读:
    AJAX聊天室小DEMO(讨厌JS,IE下有问题已解决)
    [ZT]线索二叉树(C#数据结构五)
    栈(C#数据结构学习二)
    eclipse 安装 resin 3 步骤
    解决全局utf8编码下asp.net接收gb2312乱码的问题
    模板里的控件要用FindControl(id)方法读取
    OpenSessionInView
    asp:button控件调用js函数不刷新方法
    OFFICE 出现“正在配置”的解决方法
    开发经验
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/2576655.html
Copyright © 2011-2022 走看看