• 跟我学ASP.NET MVC之六:SportsStrore添加产品目录导航


    摘要:

    上一篇文章,我建立了SportsStore应用程序的核心架构。现在我将使用这个架构向这个应用程序添加功能,你将开始看到这个基础架构的作用。我将添加重要的面向客户的简单功能,在这个过程中,你将看到MVC框架提供的额外功能。

    如果客户能够根据目录导航产品,SportsStore应用程序可用性将更高。

    • 改进ProductController控制器中的List方法,让他能够过滤在repository里的产品对象。
    • 修改改进URL格式,修改routing策略。
    • 在网站首页旁边创建一个目录列表,高亮显示当前页。

    过滤产品列表

    修改视图模型ProductsListViewModel,添加当前目录信息。

     1 using SportsStore.Domain.Entities;
     2 using System.Collections.Generic;
     3 
     4 namespace SportsStore.WebUI.Models
     5 {
     6     public class ProductsListViewModel
     7     {
     8         public IEnumerable<Product> Products { get; set; }
     9         public PagingInfo PagingInfo { get; set; }
    10         public string CurrentCategory { get; set; }
    11     }
    12 }

    修改ProductController控制器的List方法,添加参数category,根据传入的category过滤产品。

     1 using SportsStore.Domain.Abstract;
     2 using SportsStore.WebUI.Models;
     3 using System.Web.Mvc;
     4 using System.Linq;
     5 
     6 namespace SportsStore.WebUI.Controllers
     7 {
     8     public class ProductController : Controller
     9     {
    10         private IProductRepository repository;
    11 
    12         public int PageSize = 4;
    13 
    14         public ProductController(IProductRepository productRepository)
    15         {
    16             this.repository = productRepository;
    17         }
    18 
    19         public ViewResult List(string category, int page = 1)
    20         {
    21             ProductsListViewModel model = new ProductsListViewModel
    22             {
    23                 Products = repository.Products.Where(p => string.IsNullOrEmpty(category) || p.Category == category)
    24                                               .OrderBy(p => p.ProductID).Skip((page - 1) * PageSize).Take(PageSize),
    25                 PagingInfo = new PagingInfo
    26                 {
    27                     CurrentPage = page,
    28                     ItemsPerPage = PageSize,
    29                     TotalItems = category == null ? repository.Products.Count() : repository.Products.Where(e => e.Category == category).Count()
    30                 },
    31                 CurrentCategory = category
    32             };
    33             return View(model);
    34         }
    35     }
    36 }
    • 如果传入的category是空字符串,则返回所有产品列表。否则根据linq的where条件获取category下的产品列表。
    • 如果传入的category是空字符串,则返回所有产品数量。否则根据linq的where条件获取category下的产品数量。
    • 将category赋值给视图模型对象的CurrentCategory属性。

    执行程序,如果将浏览器地址栏修改成下面的形式:

    http://localhost:17596/?category=Soccer

    将返回category是Soccer的产品列表。

    很明显,用户将不会自己使用URL导航产品目录,但是你可以看到,一旦基础架构做好了,一个小的改动在一个MVC框架的应用程序起到了大的作用。

    修改定义URL格式

    没有人想看到或者使用丑陋的像/?category=Soccer一样的URL。为了改进这个,我将修改路由格式,创建更适合我和客户的生成URL的方法。

    修改RouteConfig.cs文件的RegisterRoutes方法。

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Web;
     5 using System.Web.Mvc;
     6 using System.Web.Routing;
     7 
     8 namespace SportsStore
     9 {
    10     public class RouteConfig
    11     {
    12         public static void RegisterRoutes(RouteCollection routes)
    13         {
    14             routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    15 
    16             routes.MapRoute(
    17                 name: null,
    18                 url: "",
    19                 defaults: new { controller = "Product", action = "List", category = (string)null, page = 1 }
    20             );
    21 
    22             routes.MapRoute(
    23                 name: null,
    24                 url: "Page{page}",
    25                 defaults: new { controller = "Product", action = "List", category = (string)null },
    26                 constraints: new { page = @"d+" }
    27             );
    28 
    29             routes.MapRoute(
    30                 name: null,
    31                 url: "{category}",
    32                 defaults: new { controller = "Product", action = "List", page = 1 }
    33             );
    34 
    35             routes.MapRoute(
    36                 name: null,
    37                 url: "{category}/Page{page}",
    38                 defaults: new { controller = "Product", action = "List" },
    39                 constraints: new { page = @"d+" }
    40             );
    41 
    42             routes.MapRoute(
    43                 name: "Default",
    44                 url: "{controller}/{action}/{id}",
    45                 defaults: new { controller = "Product", action = "List", id = UrlParameter.Optional }
    46             );
    47         }
    48     }
    49 }

    下面来解释添加的路由信息。

    第一个路由函数:

    url: "":表示这个是默认路由。defaults设置参数默认值,将category的空字符串设置为默认值,page的默认值是1。这样,如果访问URL:http://localhost:17596/,category为空字符串,page值为1。

    第二个路由函数:

    url: "Page{page}":表示路由的URL格式是/Page{page}。defaults设置参数category的默认值为空字符串。这样,如果访问URL:http://localhost:17596/Page2,category为空字符串,page值为传入的值2。

    第三个路由函数:

    url: "{category}":表示路由的URL格式是/{category}。defaults设置参数page的默认值为1。这样,如果访问URL:http://localhost:17596/Soccer,category为字符串Soccer,page值为默认值1。

    第四个路由函数:

    url: "{category}/Page{page}":表示路由的URL格式是/{category}/Page{page}。defaults不用设置参数默认值。这样,如果访问URL:http://localhost:17596/Soccer/Page2,category为字符串Soccer,page值为传入的值2。

    MVC使用ASP.NET路由系统来处理客户端到来的请求,但是它还用来向外生成符合URL定义格式的URL字符串,嵌入到Web页面上。于是,我可以保证应用程序的所有的URL都是一致的。

    定义好路由表后,需要修改List视图上生成链接字符串的方法调用。

     1 @model SportsStore.WebUI.Models.ProductsListViewModel
     2 
     3 @{
     4     ViewBag.Title = "Products";
     5 }
     6 
     7 @foreach (var p in Model.Products)
     8 {
     9     Html.RenderPartial("ProductSummary", p);
    10 }
    11 <div class="btn-group pull-right">
    12     @Html.PageLinks(Model.PagingInfo, x => Url.Action("List", new { page = x, category = Model.CurrentCategory }))
    13 </div>

    Url.Action方法是向外产生链接最方便的方法。这里添加将category传入生成链接的代理方法中。

    运行程序,改变浏览器地址栏上的URL,得到各路由URL页面结果。

    创建目录导航菜单

    首先创建MenuController控制器。

     1 using SportsStore.Domain.Abstract;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Web.Mvc;
     5 
     6 namespace SportsStore.WebUI.Controllers
     7 {
     8     public class NavController : Controller
     9     {
    10         private IProductRepository repository;
    11 
    12         public NavController(IProductRepository productRepository)
    13         {
    14             repository = productRepository;
    15         }
    16 
    17         public PartialViewResult Menu(string category = null)
    18         {
    19             ViewBag.SelectedCategory = category;
    20             IEnumerable<string> categories = repository.Products.Select(x => x.Category).Distinct().OrderBy(x => x);
    21             return PartialView("Menu", categories);
    22         }
    23     }
    24 }
    • 这个控制器返回PartialViewResult类型对象。PartialViewResult类跟之前使用的ViewResult类型都是继承自基类ViewResultBase。
    • ViewBag动态类型属性SelectedCategory设置为当前目录字符串。
    • 参数category将获取路由产生的URL上的目录字符串。
    • categories变量返回Product下所有目录。
    • 调用方法PartialView方法,返回PartialViewResult类型对象,传入的参数是视图名称和视图模型的变量。

    然后创建Menu视图。

     1 @model IEnumerable<string>
     2 
     3 <div>
     4     @Html.RouteLink("Home", new { controller = "Product", action = "List" }, new { @class =  "btn btn-block btn-default btn-lg" })
     5     @foreach (var link in Model)
     6     {
     7         @Html.RouteLink(link, new
     8         {
     9             controller = "Product",
    10             action = "List",
    11             category = link,
    12             page = 1
    13         }, new
    14         {
    15             @class = ("btn btn-block btn-default btn-lg") + (link == ViewBag.SelectedCategory ? " btn-primary" : "")
    16         })
    17     }
    18 </div>

    这里调用Html扩展方法RouteLink,生成链接字符串。当然,你也可以使用ActionLink方法。

    • 方法的第一个参数是链接显示的文本字符串。
    • 第二个参数是动态数据类型,用来指定控制器,控制器方法以及方法传入的参数。
    • 第三个参数也是动态数据类型,使用@class来指定链接的样式。这里使用bootstrap的样式,这里的设置顺序是:btn指定按钮样式,btn-block指定按钮呈现的外观占整行,btn-default指定按钮的皮肤颜色等,btn-lg指定按钮的大小。
    • 如果是当前页,将添加btn-primary样式,将该按钮高亮显示。

    最后,修改_Layout.cshtml文件,将Menu显示在页面上。

     1 <!DOCTYPE html>
     2 
     3 <html>
     4 <head>
     5     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     6     <link href="~/Content/bootstrap.css" rel="stylesheet" />
     7     <link href="~/Content/bootstrap-theme.css" rel="stylesheet" />
     8     <title>@ViewBag.Title</title>
     9 </head>
    10 <body>
    11     <div class="navbar navbar-inverse" role="navigation">
    12         <a class="navbar-brand" href="#">SPORTS STORE</a>
    13     </div>
    14     <div class="row panel">
    15         <div class="col-xs-3">
    16             @Html.Action("Menu", "Nav")
    17         </div>
    18         <div class="col-xs-8">
    19             @RenderBody()
    20         </div>
    21     </div>
    22 </body>
    23 </html>

    这里调用Html的扩展方法Action,传入控制器名字和方法名字,生成HTML字符串。它将Menu视图生成的字符串嵌入到div里面,生成导航栏。

    注意这里不是调用RenderPartial方法,RenderPartial方法返回的void,它跟C#语句嵌套使用,在视图上嵌入视图。

    运行程序,得到运行结果。

    如果点击Soccer目录,将返回Category是Soccer的产品列表。并将Soccer这个按钮高亮显示。

  • 相关阅读:
    apache-atlas 深度剖析
    Robot Framework自动化测试框架核心指南-如何使用Java编写自定义的RobotFramework Lib
    Hbase架构剖析
    Mysql 执行计划以及常见索引问题总结
    RobotFramework自动化测试框架-Selenium Web自动化(三)关于在RobotFramework中如何使用Selenium很全的总结(下)
    kafka connector 使用总结以及自定义connector开发
    flink 流式处理中如何集成mybatis框架
    RobotFramework自动化测试框架-Selenium Web自动化(二)关于在RobotFramework中如何使用Selenium很全的总结(上)
    一次flume exec source采集日志到kafka因为单条日志数据非常大同步失败的踩坑带来的思考
    MongoDB Java API操作很全的整理以及共享分片模式下的常见操作整理
  • 原文地址:https://www.cnblogs.com/uncle_danny/p/9048196.html
Copyright © 2020-2023  润新知