跟我学ASP.NET MVC之六:SportsStrore添加产品目录导航
摘要:
上一篇文章,我建立了SportsStore应用程序的核心架构。现在我将使用这个架构向这个应用程序添加功能,你将开始看到这个基础架构的作用。我将添加重要的面向客户的简单功能,在这个过程中,你将看到MVC框架提供的额外功能。
如果客户能够根据目录导航产品,SportsStore应用程序可用性将更高。
- 改进ProductController控制器中的List方法,让他能够过滤在repository里的产品对象。
- 修改改进URL格式,修改routing策略。
- 在网站首页旁边创建一个目录列表,高亮显示当前页。
过滤产品列表
修改视图模型ProductsListViewModel,添加当前目录信息。
using SportsStore.Domain.Entities;
using System.Collections.Generic; namespace SportsStore.WebUI.Models
{
public class ProductsListViewModel
{
public IEnumerable<Product> Products { get; set; }
public PagingInfo PagingInfo { get; set; }
public string CurrentCategory { get; set; }
}
}
修改ProductController控制器的List方法,添加参数category,根据传入的category过滤产品。
using SportsStore.Domain.Abstract;
using SportsStore.WebUI.Models;
using System.Web.Mvc;
using System.Linq; namespace SportsStore.WebUI.Controllers
{
public class ProductController : Controller
{
private IProductRepository repository; public int PageSize = ; public ProductController(IProductRepository productRepository)
{
this.repository = productRepository;
} public ViewResult List(string category, int page = )
{
ProductsListViewModel model = new ProductsListViewModel
{
Products = repository.Products.Where(p => string.IsNullOrEmpty(category) || p.Category == category)
.OrderBy(p => p.ProductID).Skip((page - ) * PageSize).Take(PageSize),
PagingInfo = new PagingInfo
{
CurrentPage = page,
ItemsPerPage = PageSize,
TotalItems = category == null ? repository.Products.Count() : repository.Products.Where(e => e.Category == category).Count()
},
CurrentCategory = category
};
return View(model);
}
}
}
- 如果传入的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方法。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing; namespace SportsStore
{
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute(
name: null,
url: "",
defaults: new { controller = "Product", action = "List", category = (string)null, page = }
); routes.MapRoute(
name: null,
url: "Page{page}",
defaults: new { controller = "Product", action = "List", category = (string)null },
constraints: new { page = @"\d+" }
); routes.MapRoute(
name: null,
url: "{category}",
defaults: new { controller = "Product", action = "List", page = }
); routes.MapRoute(
name: null,
url: "{category}/Page{page}",
defaults: new { controller = "Product", action = "List" },
constraints: new { page = @"\d+" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Product", action = "List", id = UrlParameter.Optional }
);
}
}
}
下面来解释添加的路由信息。
第一个路由函数:
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视图上生成链接字符串的方法调用。
@model SportsStore.WebUI.Models.ProductsListViewModel @{
ViewBag.Title = "Products";
} @foreach (var p in Model.Products)
{
Html.RenderPartial("ProductSummary", p);
}
<div class="btn-group pull-right">
@Html.PageLinks(Model.PagingInfo, x => Url.Action("List", new { page = x, category = Model.CurrentCategory }))
</div>
Url.Action方法是向外产生链接最方便的方法。这里添加将category传入生成链接的代理方法中。
运行程序,改变浏览器地址栏上的URL,得到各路由URL页面结果。
创建目录导航菜单
首先创建MenuController控制器。
using SportsStore.Domain.Abstract;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc; namespace SportsStore.WebUI.Controllers
{
public class NavController : Controller
{
private IProductRepository repository; public NavController(IProductRepository productRepository)
{
repository = productRepository;
} public PartialViewResult Menu(string category = null)
{
ViewBag.SelectedCategory = category;
IEnumerable<string> categories = repository.Products.Select(x => x.Category).Distinct().OrderBy(x => x);
return PartialView("Menu", categories);
}
}
}
- 这个控制器返回PartialViewResult类型对象。PartialViewResult类跟之前使用的ViewResult类型都是继承自基类ViewResultBase。
- ViewBag动态类型属性SelectedCategory设置为当前目录字符串。
- 参数category将获取路由产生的URL上的目录字符串。
- categories变量返回Product下所有目录。
- 调用方法PartialView方法,返回PartialViewResult类型对象,传入的参数是视图名称和视图模型的变量。
然后创建Menu视图。
@model IEnumerable<string> <div>
@Html.RouteLink("Home", new { controller = "Product", action = "List" }, new { @class = "btn btn-block btn-default btn-lg" })
@foreach (var link in Model)
{
@Html.RouteLink(link, new
{
controller = "Product",
action = "List",
category = link,
page = 1
}, new
{
@class = ("btn btn-block btn-default btn-lg") + (link == ViewBag.SelectedCategory ? " btn-primary" : "")
})
}
</div>
这里调用Html扩展方法RouteLink,生成链接字符串。当然,你也可以使用ActionLink方法。
- 方法的第一个参数是链接显示的文本字符串。
- 第二个参数是动态数据类型,用来指定控制器,控制器方法以及方法传入的参数。
- 第三个参数也是动态数据类型,使用@class来指定链接的样式。这里使用bootstrap的样式,这里的设置顺序是:btn指定按钮样式,btn-block指定按钮呈现的外观占整行,btn-default指定按钮的皮肤颜色等,btn-lg指定按钮的大小。
- 如果是当前页,将添加btn-primary样式,将该按钮高亮显示。
最后,修改_Layout.cshtml文件,将Menu显示在页面上。
<!DOCTYPE html> <html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="~/Content/bootstrap.css" rel="stylesheet" />
<link href="~/Content/bootstrap-theme.css" rel="stylesheet" />
<title>@ViewBag.Title</title>
</head>
<body>
<div class="navbar navbar-inverse" role="navigation">
<a class="navbar-brand" href="#">SPORTS STORE</a>
</div>
<div class="row panel">
<div class="col-xs-3">
@Html.Action("Menu", "Nav")
</div>
<div class="col-xs-8">
@RenderBody()
</div>
</div>
</body>
</html>
这里调用Html的扩展方法Action,传入控制器名字和方法名字,生成HTML字符串。它将Menu视图生成的字符串嵌入到div里面,生成导航栏。
注意这里不是调用RenderPartial方法,RenderPartial方法返回的void,它跟C#语句嵌套使用,在视图上嵌入视图。
运行程序,得到运行结果。
如果点击Soccer目录,将返回Category是Soccer的产品列表。并将Soccer这个按钮高亮显示。
跟我学ASP.NET MVC之六:SportsStrore添加产品目录导航的更多相关文章
- 跟我学ASP.NET MVC之五:SportsStrore开始
摘要: 这篇文章将介绍一个ASP.NET应用程序SportsStore的开发过程. 开始 创建解决方案 创建工程 在New ASP.NET Project - SportsStore窗口中,选择Emp ...
- 跟我学ASP.NET MVC之七:SportsStrore一个完整的购物车
摘要: SportsStore应用程序进展很顺利,但是我不能销售产品直到设计了一个购物车.在这篇文章里,我就将创建一个购物车. 在目录下的每个产品旁边添加一个添加到购物车按钮.点击这个按钮将显示客户到 ...
- 跟我学ASP.NET MVC之三:完整的ASP.NET MVC程序-PartyInvites
摘要: 在这篇文章中,我将在一个例子中实际地展示MVC. 场景 假设一个朋友决定举办一个新年晚会,她邀请我创建一个用来邀请朋友参加晚会的WEB程序.她提出了四个注意的需求: 一个首页展示这个晚会 一个 ...
- 跟我学ASP.NET MVC之二:第一个ASP.NET MVC程序
摘要: 本篇文章带你一步一步创建一个简单的ASP.NET MVC程序. 创建新ASP.NET MVC工程 点击“OK”按钮后,打开下面的窗口: 这里选择“Empty”模板以及“MVC”选项.这次不创 ...
- 跟我学ASP.NET MVC之一:开篇有益
摘要: ASP.NET MVC是微软的Web开发框架,结合了模型-视图-控制器(MVC)架构的有效性和整洁性,敏捷开发最前沿的思想和技术,以及现存的ASP.NET平台最好的部分.它是传统ASP.NET ...
- [转]我要学ASP.NET MVC 3.0(十二): MVC 3.0 使用自定义的Html控件
本文转自:http://www.cnblogs.com/lukun/archive/2011/08/05/2128693.html 概述 在ASP.NET MVC框架中已经封装了很多基于Html标 ...
- 跟我学ASP.NET MVC之十一:URL路由
摘要: 在MVC框架之前,ASP.NET假定在请求的URLs和服务器硬盘文件之间有直接的关系.服务器的职责是接收浏览器请求,从相应的文件发送输出. 这种方法只能工作于Web表单,每一个ASPX页面既是 ...
- ASP.NET MVC 5 系列 学习笔记 目录 (持续更新...)
前言: 记得当初培训的时候,学习的还是ASP.NET,现在回想一下,图片水印.统计人数.过滤器....HttpHandler是多么的经典! 不过后来接触到了MVC,便立马爱上了它.Model-View ...
- 给Asp.Net MVC及WebApi添加路由优先级
一.为什么需要路由优先级 大家都知道我们在Asp.Net MVC项目或WebApi项目中注册路由是没有优先级的,当项目比较大.或有多个区域.或多个Web项目.或采用插件式框架开发时,我们的路由注册很可 ...
随机推荐
- 阅读源码(IV)
往期系列: <由阅读源码想到> <由阅读源码想到 | 下篇> <阅读源码(III)> Eric S.Raymond的写于2014年的<How to learn ...
- svn 不能添加.a文件
1.打开终端输入 open ~/.subversion/ 2.双击打开config文件 3.修改如下两行 # global-ignores = *.o *.lo *.la *.al .libs ...
- 手动编译Flume
1.源码下载: 我用的是1.6版,因为加了kafka-sink,下载地址 http://www.apache.org/dyn/closer.cgi/flume/1.6.0/apache-flume-1 ...
- C4 垃圾回收
使用C4垃圾回收器可以有效提升对低延迟有要求的企业级Java应用程序的伸缩性. 到目前为止,stop-the-world式的垃圾回收视为影响Java应用程序伸缩性的一大障碍,而伸缩性又是现代企业级Ja ...
- unity零基础开始学习做游戏(二)让你的对象动起来
-------小基原创,转载请给我一个面子 小基认为电子游戏与电影最重要的区别就是交互,如果电子游戏没有让你输入的交互功能的话,全程都"只可远观,而不可鼓捣"的话,你可能是在看视频 ...
- Mybatis 系列6
上篇系列5中 简单看了一下TypeHandler, 本次将结束对于mybatis的配置文件的学习, 本次涉及到剩下没提及到的几个节点的配置:objectFactory.databaseIdProvid ...
- Android的颜色值转换
Android的颜色int值比较变态,是个负值,用计算机术语讲叫补码,手工转换比较麻烦,首先看看文档 https://developer.android.com/reference/android/g ...
- DjangoRestFramework实践笔记
1.Restful服务的实现方式一共三种:function based view,class based view,viewset+router,这三种实现方式的封装重度依序升高,越往后越适合典型CU ...
- gitolite服务器部署中的一些坑
1.秘钥登录问题可参考< 安装gitolite,并ssh公钥无密码登录>http://www.cnblogs.com/tr0217/p/4517952.html,该文中推荐了阮一峰的< ...
- .Net MVC5异步请求Entity Framework 无限循环解决方法
.Net MVC5异步请求Entity Framework 无限循环解决方法 Entity Framework 存在一对多.多对多之间的关系,相互依赖,在返回JSON数据时往往会出现相互引用造成的无限 ...