丹尼大叔

数学专业毕业,爱上编程的大叔,兴趣广泛。使用博客园这个平台分享我工作和业余的学习内容,以编程交友。有朋自远方来,不亦乐乎。

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

摘要:

上一篇文章,我建立了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这个按钮高亮显示。

 

posted on 2018-05-16 21:11  丹尼大叔  阅读(524)  评论(0编辑  收藏  举报