zoukankan      html  css  js  c++  java
  • 构建一个真实的应用电子商务SportsStore4

    构建一个真实的应用电子商务SportsStore(四)

    上篇中,我们将数据库中的数据显示到了 UI上,在这里我要强调一点,在上篇中我们应用了强类型的View,不要与model业务混淆,有关强类型view的知识点,不在本实例范畴之内,请参阅相关文档。对于任何一个电子商务网站来说,都需要使用户能方便的浏览所有的商品,并能够从一页迁移到另一页,这是个非常实用、也非常基本的功能,但在MVC4中,怎么实现它呢,现在就让我们一步一步的完善这个功能。
     
    首先,我们要为我们的Product控制器的List 方法添加一个参数,用它来代表浏览的页号,代码如下:
    复制代码
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Mvc;
    using SportsStore.Domain.Abstract;
    using SportsStore.Domain.Entities;
    using SportsStore.WebUI.Models;
    
    namespace SportsStore.WebUI.Controllers
    {
        public class ProductController : Controller
        {
            private IProductsRepository repository;
            public int PageSize = 4;
    
            public ProductController(IProductsRepository productRepository)
            {
                this.repository = productRepository;
            }
    
            public ViewResult List(int page = 1) {
    
                return View(repository.Products.OrderBy(p => p.ProductID)
                                    .Skip((page - 1) * PageSize)
                                    .Take(PageSize));
            }
    
        }
    }
    复制代码
    PageSize字段指定了每一页要显示的产品数量,稍后我们将使用更好的机制来替换它,现在你只需要理解它。
    我们还添加了一个可选的参数到List方法,这就表示如果我们调用的方法没有参数(List()), 我们将会使用(List(1)) 来处理,就是默认为显示第一页。
     
    现在我们添加一个测试文件到你的SportsStore.UnitTests工程
    复制代码
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using Moq;
    using SportsStore.Domain.Abstract;
    using SportsStore.Domain.Entities;
    using SportsStore.WebUI.Controllers;
    using System.Collections.Generic;
    using System.Linq;
    using SportsStore.WebUI.Models;
    using System;
    
    namespace SportsStore.UnitTests {
        [TestClass]
        public class UnitTest1 {
            [TestMethod]
            public void Can_Paginate() {
                    // Arrange
                    Mock<IProductsRepository> mock = new Mock<IProductsRepository>();
                    mock.Setup(m => m.Products).Returns(new Product[] {
                    new Product {ProductID = 1, Name = "P1"},
                    new Product {ProductID = 2, Name = "P2"},
                    new Product {ProductID = 3, Name = "P3"},
                    new Product {ProductID = 4, Name = "P4"},
                    new Product {ProductID = 5, Name = "P5"}
                    }.AsQueryable());
                    ProductController controller = new ProductController(mock.Object);
                    controller.PageSize = 3;
                    // Act
                    IEnumerable<Product> result =
                    (IEnumerable<Product>)controller.List(2).Model;
                    // Assert
                    Product[] prodArray = result.ToArray();
                    Assert.IsTrue(prodArray.Length == 2);
                    Assert.AreEqual(prodArray[0].Name, "P4");
                    Assert.AreEqual(prodArray[1].Name, "P5");
            }
    
    
        }
    }
    复制代码
    这里请注意看,我们是如何轻松的从一个控制器的结果集中获得数据的,在这里,我们反转了这个结果集到一个数组,并检查单个对象的长度和值。
    运行工程,可以看到如下结果:
     
    添加View Model
    View Model 不是我们领域模型的一部分,只是为了方便在控制器和View 之间传递数据,所以我们把它放在SportsStore.WebUI工程的Models文件夹中,命名为PagingInfo,代码如下:
    复制代码
    using System;
    
    namespace SportsStore.WebUI.Models {
    
        public class PagingInfo {
    
            public int TotalItems { get; set; }
            public int ItemsPerPage { get; set; }
            public int CurrentPage { get; set; }
    
            public int TotalPages
            {
                get { return (int)Math.Ceiling((decimal)TotalItems / ItemsPerPage); }
            }
    
        }
    }
    复制代码
    添加HTML Helper 方法
    现在,我们需要添加一个文件夹命名为HtmlHelpers,并添加一个文件,命名为PagingHelpers。
    复制代码
    using System;
    using System.Text;
    using System.Web.Mvc;
    using SportsStore.WebUI.Models;
    namespace SportsStore.WebUI.HtmlHelpers
    {
        public static class PagingHelpers
        {
            public static MvcHtmlString PageLinks(this HtmlHelper html,
            PagingInfo pagingInfo,
            Func<int, string> pageUrl)
            {
                StringBuilder result = new StringBuilder();
                for (int i = 1; i <= pagingInfo.TotalPages; i++)
                {
                    TagBuilder tag = new TagBuilder("a"); // Construct an <a> tag
                    tag.MergeAttribute("href", pageUrl(i));
                    tag.InnerHtml = i.ToString();
                    if (i == pagingInfo.CurrentPage)
                        tag.AddCssClass("selected");
                    result.Append(tag.ToString());
                }
                return MvcHtmlString.Create(result.ToString());
            }
        }
    }
    复制代码
    这个PageLinks 扩展方法为页链接集合产生HTML,这个页链接集合使用了PagingInfo对象, Func 参数提供了传递代理的能力,代理被用来产生链接到其他页面的链接。
     
    测试我们的HtmlHelpers
    在我们测试文件中添加如下引用和方法:
    复制代码
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using Moq;
    using SportsStore.Domain.Abstract;
    using SportsStore.Domain.Entities;
    using SportsStore.WebUI.Controllers;
    using System.Collections.Generic;
    using System.Linq;
    using SportsStore.WebUI.Models;
    using System;
    using System.Web.Mvc;
    using SportsStore.WebUI.HtmlHelpers;
    
    namespace SportsStore.UnitTests {
        [TestClass]
        public class UnitTest1 {
            [TestMethod]
            public void Can_Paginate() {
                    // Arrange
                    Mock<IProductsRepository> mock = new Mock<IProductsRepository>();
                    mock.Setup(m => m.Products).Returns(new Product[] {
                    new Product {ProductID = 1, Name = "P1"},
                    new Product {ProductID = 2, Name = "P2"},
                    new Product {ProductID = 3, Name = "P3"},
                    new Product {ProductID = 4, Name = "P4"},
                    new Product {ProductID = 5, Name = "P5"}
                    }.AsQueryable());
                    ProductController controller = new ProductController(mock.Object);
                    controller.PageSize = 3;
                    // Act
                    IEnumerable<Product> result =
                    (IEnumerable<Product>)controller.List(2).Model;
                    // Assert
                    Product[] prodArray = result.ToArray();
                    Assert.IsTrue(prodArray.Length == 2);
                    Assert.AreEqual(prodArray[0].Name, "P4");
                    Assert.AreEqual(prodArray[1].Name, "P5");
            }
    
    
            [TestMethod]
            public void Can_Generate_Page_Links()
            {
                // Arrange - define an HTML helper - we need to do this
                // in order to apply the extension method
                HtmlHelper myHelper = null;
                // Arrange - create PagingInfo data
                PagingInfo pagingInfo = new PagingInfo
                {
                    CurrentPage = 2,
                    TotalItems = 28,
                    ItemsPerPage = 10
                };
                // Arrange - set up the delegate using a lambda expression
                Func<int, string> pageUrlDelegate = i => "Page" + i;
                // Act
                MvcHtmlString result = myHelper.PageLinks(pagingInfo, pageUrlDelegate);
                // Assert
                Assert.AreEqual(result.ToString(), @"<a href=""Page1"">1</a>"
                + @"<a class=""selected"" href=""Page2"">2</a>"
                + @"<a href=""Page3"">3</a>");
            }
        }
    }
    复制代码
    要使扩展方法有效,在代码中,我们需要确保引用它所在的namespace,我使用了using语句,但是对于一个 Razor View,我们必须配置web.config文件,而一个MVC工程,有2个web.config文件,一个是在工程的根目录下,另一个在View文件夹中,我们需要配置文件夹中的这个,添加如下语句到namespace标签中:
    <add namespace="SportsStore.WebUI.HtmlHelpers"/>
    复制代码
    <namespaces>
            <add namespace="System.Web.Mvc" />
            <add namespace="System.Web.Mvc.Ajax" />
            <add namespace="System.Web.Mvc.Html" />
            <add namespace="System.Web.Optimization"/>
            <add namespace="System.Web.Routing" />
            <add namespace="SportsStore.WebUI.HtmlHelpers"/>
          </namespaces>
    复制代码
     
    添加View Model数据
    我们并没有打算完全使用HTML helper方法,我们依然需要提供一个PagingInfo view model类的实例到View,我们可以使用view bag的特性,但我们更倾向于打包所有的Controller数据发送到一个单一的View,要实现这一点,我们要添加一个新类到Model文件夹,命名为ProductsListViewModel。
     
    好了,现在我们再更新一下ProductController的代码,用ProductsListViewModel类提供给View更详细的数据。
     
    复制代码
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Mvc;
    using SportsStore.Domain.Abstract;
    using SportsStore.Domain.Entities;
    using SportsStore.WebUI.Models;
    
    namespace SportsStore.WebUI.Controllers
    {
        public class ProductController : Controller
        {
            private IProductsRepository repository;
            public int PageSize = 4;
    
            public ProductController(IProductsRepository productRepository)
            {
                this.repository = productRepository;
            }
    
            public ViewResult List(int page = 1) {
    
                ProductsListViewModel model = new ProductsListViewModel
                {
                    Products = repository.Products
                                        .OrderBy(p => p.ProductID)
                                        .Skip((page - 1) * PageSize)
                                        .Take(PageSize),
                                        PagingInfo = new PagingInfo
                                        {
                                            CurrentPage = page,
                                            ItemsPerPage = PageSize,
                                            TotalItems = repository.Products.Count()
                                        }
                };
                return View(model);
            }
    
        }
    }
    复制代码


    修改测试文件代码:

    复制代码
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using Moq;
    using SportsStore.Domain.Abstract;
    using SportsStore.Domain.Entities;
    using SportsStore.WebUI.Controllers;
    using System.Collections.Generic;
    using System.Linq;
    using SportsStore.WebUI.Models;
    using System;
    using System.Web.Mvc;
    using SportsStore.WebUI.HtmlHelpers;
    
    namespace SportsStore.UnitTests {
        [TestClass]
        public class UnitTest1 {
            [TestMethod]
            public void Can_Paginate() {
                    // Arrange
                    Mock<IProductsRepository> mock = new Mock<IProductsRepository>();
                    mock.Setup(m => m.Products).Returns(new Product[] {
                    new Product {ProductID = 1, Name = "P1"},
                    new Product {ProductID = 2, Name = "P2"},
                    new Product {ProductID = 3, Name = "P3"},
                    new Product {ProductID = 4, Name = "P4"},
                    new Product {ProductID = 5, Name = "P5"}
                    }.AsQueryable());
                    ProductController controller = new ProductController(mock.Object);
                    controller.PageSize = 3;
    
                    // Action
                    ProductsListViewModel result = (ProductsListViewModel)controller.List(2).Model;
    
                    // Assert
                    Product[] prodArray = result.Products.ToArray();
                    Assert.IsTrue(prodArray.Length == 2);
                    Assert.AreEqual(prodArray[0].Name, "P4");
                    Assert.AreEqual(prodArray[1].Name, "P5");
            }
    
            [TestMethod]
            public void Can_Send_Pagination_View_Model()
            {
                // Arrange
                Mock<IProductsRepository> mock = new Mock<IProductsRepository>();
                mock.Setup(m => m.Products).Returns(new Product[] {
                                                                new Product {ProductID = 1, Name = "P1"},
                                                                new Product {ProductID = 2, Name = "P2"},
                                                                new Product {ProductID = 3, Name = "P3"},
                                                                new Product {ProductID = 4, Name = "P4"},
                                                                new Product {ProductID = 5, Name = "P5"}
                                                                }.AsQueryable());
                // Arrange
                ProductController controller = new ProductController(mock.Object);
                controller.PageSize = 3;
                // Act
                ProductsListViewModel result = (ProductsListViewModel)controller.List(2).Model;
                // Assert
                PagingInfo pageInfo = result.PagingInfo;
                Assert.AreEqual(pageInfo.CurrentPage, 2);
                Assert.AreEqual(pageInfo.ItemsPerPage, 3);
                Assert.AreEqual(pageInfo.TotalItems, 5);
                Assert.AreEqual(pageInfo.TotalPages, 2);
            }
    
            [TestMethod]
            public void Can_Generate_Page_Links()
            {
                // Arrange - define an HTML helper - we need to do this
                // in order to apply the extension method
                HtmlHelper myHelper = null;
                // Arrange - create PagingInfo data
                PagingInfo pagingInfo = new PagingInfo
                {
                    CurrentPage = 2,
                    TotalItems = 28,
                    ItemsPerPage = 10
                };
                // Arrange - set up the delegate using a lambda expression
                Func<int, string> pageUrlDelegate = i => "Page" + i;
                // Act
                MvcHtmlString result = myHelper.PageLinks(pagingInfo, pageUrlDelegate);
                // Assert
                Assert.AreEqual(result.ToString(), @"<a href=""Page1"">1</a>"
                + @"<a class=""selected"" href=""Page2"">2</a>"
                + @"<a href=""Page3"">3</a>");
            }
        }
    }
    复制代码
    运行程序,你将看到如下结果:
     
    改进我们的页面链接
    我们的页面链接都是工作的,但它看起来是这样的:
    http://localhost/?page=2
    我们能做的更好些, 尤其是通过创建一个scheme,它遵循可组装URLs. 这使得用户更容易理解,并且更有效率,它看起来应该像下面的地址:
    http://localhost/Page2
    MVC很容易去改变URL scheme,因为它使用了ASP.NET的routing特性,我们要做的就是添加一个新的route 到RouteConfig.cs文件的RegisterRoutes,打开App_Start文件夹,找到我们要改的文件。
    复制代码
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Mvc;
    using System.Web.Routing;
    
    namespace SportsStore.WebUI
    {
        public class RouteConfig
        {
            public static void RegisterRoutes(RouteCollection routes)
            {
                routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    
                routes.MapRoute(
                    name: null,
                    url: "Page{page}",
                    defaults: new { Controller = "Product", action = "List" }
                    );
    
                routes.MapRoute(
                    name: "Default",
                    url: "{controller}/{action}/{id}",
                    defaults: new { controller = "Product", action = "List", id = UrlParameter.Optional }
                );
            }
        }
    }
    复制代码

    把我的Route放在默认的Route前是很重要的,Route的处理是按照它们被列出的顺序进行的,我们需要用我们新的Route优先于默认的,现在你暂时先了解这些,以后,我们会更加详细的讲解它。

     好了,这个博客的编辑器太难用了,总是不停的刷新,无法固定页面位置,今天的内容比较多,希望大家能仔细看好每一步,这里面没有一个字是多余的!剩下没写完的下次再写吧!请继续关注的续篇。
     
     
     
     
    标签: MVC4NinjectEFMoqIOCJquery
  • 相关阅读:
    vue 点击出现下拉菜单,点击下拉菜单以外都要关闭菜单
    关于SET ANSI_PADDING的用法
    VUE中的/deep/用法
    VUE中的/deep/用法
    数字与字符串系列教材 (十)- 自己开发一个Java StringBuffer
    数字与字符串系列教材 (九)- Java StringBuffer常见方法
    数字与字符串系列教材 (八)- Java 比较字符串详解
    数字与字符串系列教材 (七)- Java常见字符串方法
    数字与字符串系列教材 (六)- Java中的字符串String详解
    数字与字符串系列教材 (六)- Java中的字符串String详解
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3115704.html
Copyright © 2011-2022 走看看