SQL Server【MVC 4】5.SportsSore —— 一个实事求是的应用程序

 作者:[美]Adam Freeman
     来源:《精通ASP.NET MVC
4》

前建立的还是概括的MVC程序,现在及了吧所有事务综合在一起,以树立一个简短但真正的电子商务应用程序的时段了。

每当是打算建立之应用程序 — SportsStore
(体育用品商店),将遵循随处可见的在线公司所动的藏方式。将开创一个客户可以透过分类与页面进行浏览的在线产品分类,一个客户可以加上和去商品之购物车,和一个客户能输入其右击地址细节的结算页面。另外,还用创造一个蕴含创建、读取、更新和去功能的管理区,以便对活分类进行保管并针对拖欠区域开展保护,以使只出记名的管理员才会拓展改动。

此书打算建立之之应用程序不只是一个浮泛的言传身教,而是要创造一个长盛不衰且真正的、符合当下最为实用要求的应用程序。由于要成立必要之根结构,一开始的速度会发出硌款。的确,若采用
WebForm,则可以更快地建初期的成效,只要拖放一些及数据库直接绑定的半空中即可。但每当
MVC
应用程序中所授的这些前期工作,会带可保护、可扩大及组织可以的代码,且这些代码对单元测试具有突出支持。一旦恰当地建好了这种基本的底部架构,后面的事体虽会赶紧起来了。

1.开始

1.1 创建 Visual Studio 解决方案和类型

正文打算创建一个饱含三只品类的Visual Studio
解决方案,一个路保含域模型,一个凡是MVC
应用程序,第三单则带有单元测试。首先用”空白解决方案”模板创建一个称为也”SportsStore”的新的Visual
Studio 解决方案。

SQL Server 1

Visual Studio
解决方案是一个带有一个要么多只类别之容器。示例应用程序需要三独品类,如下图所示:

SQL Server 2

SQL Server 3

 

1.2 添加引用

参照前的博文 【MVC 4】3.MVC 基本工具(创建示范项目、使用
Ninject) 
和 【MVC 4】4.MVC 基本工具(Visual Studio
的单元测试、使用Moq) 
对库和类别做好对的援。所需要的花色依赖性如下图所示:

SQL Server 4

 

1.3 设置DI容器

前面的文章 【MVC 4】3.MVC 基本工具(创建示范项目、使用
Ninject)
展示了如何使 Ninject 创建一个自定义依赖性解析器,以便 MVC
框架用她创建整个应用程序实例化对象。这里打算利用不同的道,即创办一个自定义的控制器工厂。用户可在其间添加起定义代码,以改MVC框架的(默认)行为,或者如这里所开的平等,将DI
限制及应用程序的平等局部。常用的模式是因此依赖性解析器来处理
DI,而用由定义控制器工厂来转查找控制器类的点子,但以此例中,只打算以控制器工厂。

于 SportsStore.WebUI
项目受到开创一个称呼”Infreastructure”的公文夹,然后创建一个称为吧”NinjectContrillerFactory
(Ninject 控制器工厂)”的类似。

using Ninject;
using System;
using System.Web.Mvc;
using System.Web.Routing;

namespace SportsStore.WebUI.Infrastructure
{
    public class NinjectControllerFactory : DefaultControllerFactory
    {
        private IKernel ninjectKernel;

        public NinjectControllerFactory()
        {
            ninjectKernel = new StandardKernel();
            AddBindings();
        }

        protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
        {
            return controllerType == null
                ? null
                : (IController)ninjectKernel.Get(controllerType);
        }

        private void AddBindings()
        {
            //put bindings here

        }
    }
}

 

尽管如此尚无添加任何绑定,但当得经常,可以应用AddBindings 方法。必须报 MVC
希望用此 NinjectControllerFactory 类来创造控制器对象,其方法是以
SportsStore.WebUI 项目蒙 Global.asax.cs 文件的Application_Start
方法中上加有代码,如下粗体部分所示:

using SportsStore.WebUI.Infrastructure;using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;

namespace SportsStore.WebUI
{
    // 注意: 有关启用 IIS6 或 IIS7 经典模式的说明,
    // 请访问 http://go.microsoft.com/?LinkId=9394801

    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            WebApiConfig.Register(GlobalConfiguration.Configuration);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);

            ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory());
        }
    }
}

 

1.4 运行程序

运作程序,会盼一个错误页面,这是以所要的 URL 是跟 Ninject
尚未开展绑定的控制器相关联的:

SQL Server 5

一经进展到即同一步,说明 Visual Studio 2012 和ASP.NET MVC
开发条件的准备工作展开的老大万事如意。

 

2.从域模型开始

MVC
应用程序中产生极度多之工作还是围绕域模型如果进行的,因此,域模型是始工作的最佳位置。

由当时是一个电子商务应用程序,因此用的不过强烈的域实体是产品(Product)。在SportsSore.Domain
项目面临开创一个名为吧 “Entities”
的新文件夹,然后以其间创建一个叫作吧“Product”的切近。

namespace SportsStore.Domain.Entities
{
    public class Product
    {
        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; }
    }
}

上述清单遵循了于共同独立的 Visual Studio
项目中定义域模型的预定,即类必须标记为
public,虽然未必然要是准这同约定,但这么做推波助澜维持模型和控制器分离。

 

2.1 创建一个华而不实的存储库

现在亟需某种方式来获取数据库中之Product
实体。正而前博文所诠释的,人们愿意持久化逻辑与域模型实体是分离的——此事经过使用存储库模式来促成。此刻,不必顾虑会咋样实现持久化,不过,将从概念其的接口来起就同样经过。

以 SportsStore.Domain 项目蒙开创一个誉为吧 Abstract
的顶层新文件夹,并创立一个名叫吧 IProductRepository 的新接口,代码如下:

using SportsStore.Domain.Entities;
using System.Linq;

namespace SportsStore.Domain.Abstract
{
    public interface IProductRepository
    {
        IQueryable<Product> Products { get; }
    }
}

该接口使用了 IQueryable<T> 接口,以便能赢得同等名目繁多 Product
对象,而不用说明数据如何存储、存储于何处,以及哪接收数据。使用这同一
IProductRepository 接口的类似,可以得到 Product
对象要不必知道它来自哪里或什么递交它们,这是存储库模式之本来面目。在添加特性的上上下下开发过程中,将再度审视这同样接口。

 

2.2 创建模仿存储库

而今,已经定义了一个虚无接口,可以兑现持久化机制,并将其挂接到一个数据库。本文打算以后头有再举行就桩业务。为了能够开编制应用程序的任何有,本文打算创建一个
IProductRepository 接口的套实现。本文打算以 SportsStore.WebUI 项目之
NinjectControllerFactory 类的 AddBindings 方法中举行就宗事,代码如下:

using Moq;
using Ninject;
using SportsStore.Domain.Abstract;
using SportsStore.Domain.Entities;
using System;
using System.Linq;
using System.Collections.Generic;
using System.Web.Mvc;
using System.Web.Routing;

namespace SportsStore.WebUI.Infrastructure
{
    public class NinjectControllerFactory : DefaultControllerFactory
    {
        private IKernel ninjectKernel;

        public NinjectControllerFactory()
        {
            ninjectKernel = new StandardKernel();
            AddBindings();
        }

        protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
        {
            return controllerType == null
                ? null
                : (IController)ninjectKernel.Get(controllerType);
        }

        private void AddBindings()
        {
            //put bindings here
            Mock<IProductRepository> mock = new Mock<IProductRepository>();
            mock.Setup(m=>m.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());
            ninjectKernel.Bind<IProductRepository>().ToConstant(mock.Object);
        }
    }
}

此处必须对该问卷添加一些命名空间,但因此来创造模仿存储库实现的进程采用的是 【MVC
4】4.MVC 基本工具(Visual Studio
的单元测试、使用Moq)
所介绍的均等的 Moq 技术。AsQueryable方法是一个 LINQ 扩展方法,它用
IEnumerable<T> 转换成
IQueryable<T>,此处需要它来配合接口签名。

人人愿意,Ninject 无论何时收到及一个 IProductRepository
接口实现的请求,都回同样的拟目标,这就是是运用 ToConstant 方法的因由

...
ninjectKernel.Bind<IProductRepository>().ToConstant(mock.Object);
...

Ninject 会一直因该法对象来满足对 IProductRepository
接口的求,而休是每次都创造一个新的落实目标实例。

 

3.著产品列表

仍小结将创设一个控制器和一个动作方法,它亦可亮存储库中的产品细节。此刻,将只只针对模仿存储库中的多寡。

3.1 添加控制器

新建控制器”ProductController”,模板也”空 MVC 控制器”,修改代码如下

using SportsStore.Domain.Abstract;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace SportsStore.WebUI.Controllers
{
    public class ProductController : Controller
    {
        private IProductRepository repository;

        public ProductController(IProductRepository productRepository)
        {
            this.repository = productRepository;
        }

        public ViewResult List()
        {
            return View(repository.Products);
        }
    }
}

 像这样调用 View
方法(未指定视图名称),是喻框架为该动作方法渲染一个默认视图。通过将
Product 对象的列表传递让这个 View
方法,这是于为框架提供数据,以便用这些数据填强类型视图中的 Model
对象。

 

3.2 添加视图

而今需吗 List 动作方法上加默认视图。添加对应的视图文件 List.cshtml
,并渲染视图文件如下:

@model IEnumerable<SportsStore.Domain.Entities.Product>

@{
    ViewBag.Title = "Products";
}

@foreach (var p in Model) { 
<div class="item">
    <h3>@p.Name</h3>
    @p.Description
    <h4>@p.Price.ToString("c")</h4>
</div>
}

 

3.3 设置默认路由于

今日一经开的万事办事是报 MVC 框架,抵达网站到底的伸手应该于射到
ProductController 类的List 动作方法齐。这足以经过编制 Global.asax.cs 的
RegisterRoutes 方法实现,代码如下:

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: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Product", action = "List", id = UrlParameter.Optional }
            );
        }
    }
}

 

3.4 运行应用程序

至今,所有基础工作都已稳。此刻早就生一个含有一个动作方法的控制器,该动作方法以默认
URL
被请时受调用,它依靠让储存库接口的一个套实现,该存储库接口生成了部分简短的测试数据。这些测试数据给传送给跟动作方法关联在共的视图,而视图对每个产品创建一个概括的细节列表。运行该应用程序,效果图如下:

SQL Server 6

即时是ASP.NET MVC 框架典型的支出模式。

 

4.备数据库

眼前已经可以显示含有产品细节之简约视图,但那个形的知识模仿的
IProductRepository
所返的测试数据。在好显示真实数据的存储库之前,还需树立一个数据库,并为此有数目填它。

本文打算盖 SQL Server 作为数据库,并据此 Entity Framework(实体框架 –
EF)来做客数据库,EF 是 .NET 的ORM(对象关系映射)框架。ORM
框架为开发人员可以用规则的C# 对象来行使关系数据库的说明、列和实行。 LINQ
可以跟差之数据源一起坐班,其中有就是是 Entity Framework 。

 

4.1 创建数据库

增补到 Visual Studio 2012 和 SQL Server 2012 中之一个生好的风味是
LocalDB。它是特地为开发者而计划的一个勿管理的SQL Server
核心功能实现。使用该特性使我们当树项目、以及后将数据库部署到完全版本的
SQL Server 期间,可以超越了数据库的装置过程。

先是个步骤是以 Visual Studio
中开创数据库连接。从”View(视图)”菜单中开辟”Database
Explorer(数据库资源管理器)”窗口,点击”Connect to
Database(连接到数据库)“按钮。

据悉提示,登录数据库,并新建数据库 SportsStore

SQL Server 7

 

4.2 定义数据库方案

新建的数据库只待一个数据表,用以存储 Product
数据。右击数据库对应的”Tables(表)”条目,新增数据表。

SQL Server 8

就将会晤显得创建新表的设计器。使用 T-SQL 窗口,输入相应的SQL语句创建数量表
Products 。

SQL Server 9

点击左上角的”更新”按钮,会视该语句之功用摘要。

SQL Server 10

点击”更新数据库”,以实践该 SQL 语句,并当数据库被开创 Products 表。

SQL Server 11

 

4.3 向数据库添加数据

本文打算对拖欠数据库手工添加一些数量。

每当“数据库资源管理器”窗口中,展开 SportsStore 数据库的“表”条目,右击
Products 表,选择“显示表数据”,然后输入下图所显示数据。可以用 Tab
键逐行移动光标。在一行的末梢仍 Tab
键,将更换到下一行并创新数据库中之数码。

 SQL Server 12

 

4.4 创建实体框架上下文

Entity Framework
的风靡版本包含了一个名为“Code-first(代码先行)”的死去活来好的特性。其考虑是得先行定义模型中的类,然后重新经这些类生成数据库。

当时不行适合绿地(Green-field)开发项目,但这些类别并无多呈现。因此,本文打算演示下
Code-First 的同一栽变异,以此将模型类与存活的数据库关联在联合。

首先步,是以Entity Framework (此处是6.1本子)添加到 SportsStore.Domain
项目面临。通过 管理NuGet 包,安装时的 Entity Framework 包。

SQL Server 13

 

下一个手续是创办一个用前方建立的简短模型和数据库关联起来的内外文类(Context
Class)。

缔造一个初文件夹
“Concrete”,并当里头添加一个称呼吧”EFDbContext”的新类。代码如下:

using SportsStore.Domain.Entities;
using System.Data.Entity;

namespace SportsStore.Domain.Concrete
{
    public class EFDbContext:DbContext
    {
        public DbSet<Product> Products { get; set; }
    }
}

以用 Code-First 特性,需要创造一个派生于
System.Data.Entity.DbContext
的类。这个类会为用户只要采取的数据库中之每个表自动地定义一个性。

欠属性指定了表名,并将 DbSet
结果的型参数指定为实体框架用来代表表行的型。在这个事例中,该属性名是
Products(数据库中之表名)。即,希望就此 Product 模型类型来表示 Products
表的一一行。

内需报 Entity Framework 如何连接到数据库,为了成功就同行事,只待以
SportsStore.WebUI 项目之 Web.config
文件被上述下文类同样的讳添加同长达数据库连接字符串即可,如下所示

  <connectionStrings>
    <add name="EFDbContext" connectionString="Data Source=(localdb)\v11.0;Initial Catalog=SportsStore;Integrated Security=True" 
providerName="System.Data.SqlClient" />
  </connectionStrings>

 

4.5 创建 Product 存储库

本,本文就办好了实在落实 IProductRepository 类所要之各种准备。 在
SportsStore.Domain 项目的 Concrete
文件夹着上加一个近乎,取名”EFProductRepository”,代码如下:

using SportsStore.Domain.Abstract;
using SportsStore.Domain.Entities;
using System.Linq;

namespace SportsStore.Domain.Concrete
{
    public class EFProductRepository:IProductRepository
    {
        private EFDbContext context = new EFDbContext();
        public IQueryable<Product> Products {
            get { return context.Products; }
        }
    }
}

当下就算是储存库类,它实现了 IProductRepository 接口,并应用了一个
EFDbContext 实例,以便用 Entity Framework
接收数据库底数据。在针对该存储库添加特性时,便会相此是什么行使 Entity
Framework 的。

最终一步是吧 Ninject 对模拟存储库底绑定替换为对实在存储库的绑定。编辑
SportsStore.WebUI 项目遭到之 NinjectControllerFactory 类,使 AddBindings
方法如下所示:

using Moq;
using Ninject;
using SportsStore.Domain.Abstract;
using SportsStore.Domain.Entities;
using System;
using System.Linq;
using System.Collections.Generic;
using System.Web.Mvc;
using System.Web.Routing;
using SportsStore.Domain.Concrete;

namespace SportsStore.WebUI.Infrastructure
{
    public class NinjectControllerFactory : DefaultControllerFactory
    {
        private IKernel ninjectKernel;

        public NinjectControllerFactory()
        {
            ninjectKernel = new StandardKernel();
            AddBindings();
        }

        protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
        {
            return controllerType == null
                ? null
                : (IController)ninjectKernel.Get(controllerType);
        }

        private void AddBindings()
        {
            //put bindings here
            ninjectKernel.Bind<IProductRepository>().To<EFProductRepository>();
        }
    }
}

乍的绑定以粗体显示,它报告我 Ninject ,用户要创建 EFProductRepository
类的实例来针对 IProductRepository
接口的伸手进行服务。再次运行应用程序,效果如下:

SQL Server 14

 

顾:记得更新 SportsStore.WebUI 项目中
Entity Framework的版本,与 SportsStore.Domain
项目中之本子保持一致。不然报错。

 

5.添加分页

起上图可以观看,数据库中的保有产品还亮在一个纯净的页面及。本小结将添加对分页的支持,以便在一个页面及展示得数额的制品,用户可以逐页查看所有产品分类。要促成这或多或少,可以在
Product 控制器中的 List 方法上上加一个参数,如下所示:

using SportsStore.Domain.Abstract;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace SportsStore.WebUI.Controllers
{
    public class ProductController : Controller
    {
        private IProductRepository repository;
        //指明用户希望每页显示4个产品
        public int PageSize = 4;

        public ProductController(IProductRepository productRepository)
        {
            this.repository = productRepository;
        }

        public ViewResult List(int page=1)
        {
            //从存储库获取 Product 对象,
            //按主键顺序排序,略过起始页之前出现的产品数,
            //然后取出由 PaeSize 字段指定的产品个数
            return View(repository.Products.OrderBy(p=>p.ProductID).Skip((page-1)*PageSize).Take(PageSize));
        }
    }
}

 

5.1 显示页面链接

倘运行是应用程序,将见到只有四独章显示在页面及。如果想查其他一样页,可以将询问字符串参数加到
URL 的终极,如下所示:

http://localhost:64245/?page=2

急需改 URL 的捧口号,使的同方周转的 ASP.NET
开发服务器端口号匹配。运用这种查询字符串,可以对全体产品分类进行导航。

如若以方便客户。需要在每个产品列表的底部渲染一些页面的链接,以要客户可以在不同之页面内导航。为了达到这无异目的,本文打算实现一个但选用的
HTML 辅助器方法,它仿佛于事先 【MVC 4】1.率先只 MVC
应用程序 中以的
Html.TextBoxFor 和 Html.BeginForm
方法。该辅助器方法以为所急需之导航链接生成 HTNL 标记。

(1) 添加视图模型

以支持 HTML
辅助器方法,本文打算把可用页面数、当前页、已经储存库中产品总数等方面的音信传送给视图。做这种从极其爱的办法是创办一个视图模型,在
SportsStore.WebUI 文件夹 Models 中新建类文件 PagingInfo.cs ,代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

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); }
        }
    }
}

视图模型并无是地方模型的同一组成部分,它只是一样种植有益在控制器和视图之间传递数据的近乎。为了强调这一点,将是看似位居
SportsStore.WebUI 项目蒙,以要它与域模型的类分离开(将视图模型在 MVC
框架项目的 Models
文件夹,而休是坐落类库项目被,这种做法可以说明视图模型不是地方模型,明确了概念,也要应用程序的组织更清楚)。

 

(2)添加 HTML 辅助器方法

本来了此视图模型,便足以兑现此 HTML
辅助器方法了,该措施称为“PageLinks”。在 SportsStore.WebUI
项目遭到开创一个新文件夹“HtmlHelpers”,并加上一个初的静态类“PagingHelpers(分页辅助器)”。类公事的情节如下所示:

using SportsStore.WebUI.Models;
using System;
using System.Text;
using System.Web.Mvc;

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"); // 构造一个<a>标签
                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 扩展方法应用 PagingInfo
对象吃提供的音讯异常成一组页面链接的 HTML 。Func
参数提供了以其中传递委托的力量,该信托用于转移查看其他页面的链接。

 

特生隐含扩展方法的命名空间在限制外经常,其中的恢宏方法才是可用之。在一个代码文件被,这是为此
using 语句来成功的;但对于一个 Razor 视图,必须管一个安排条目添加到
Web.config 文件被,或当是视图上加加同长长的 @using 语句
。容易混淆视听的凡,在一个 Razor 的 MVC 项目蒙发生三三两两独 Web.config
文件:主配置文件在应用程序的绝望目录,而视图专用的配置文件在 Views
文件夹。需要改的是 Views/Web.config 文件,如下所示:

  <system.web.webPages.razor>
    <host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
    <pages pageBaseType="System.Web.Mvc.WebViewPage">
      <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>
    </pages>
  </system.web.webPages.razor>

每当一个 Razor
视图中待引用的各级一个命名空间,都需要盖这种措施开展宣示,或在视图中之所以
@using 语句子进行宣示。

 

(3)添加视图模型视图

当前还不曾办好利用 HTML 辅助器方法的备选,还需要将这 PagingInfo
视图模型类的一个实例提供给视图。可以为此 View Bag
(视图包)特性来开这档子事,但是一个重新好的法门是将控制器发送给视图所有数据封装成一个十足的视图模型类。为夫,需要拿一个初的称为吧“ProductsListViewModel”
的切近添加至 SportsStore.WebUI 的 Models 文件夹。

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; }
    }
}

现今,可以创新 ProductController 类中的 List 方法,以便使这
ProductsListViewModel
类,给视图提供在页面上显示的出品细节及分页细节,修改后代码如下:

using SportsStore.Domain.Abstract;
using SportsStore.WebUI.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace SportsStore.WebUI.Controllers
{
    public class ProductController : Controller
    {
        private IProductRepository repository;
        public int PageSize = 4;

        public ProductController(IProductRepository 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);
        }
    }
}

这些改动将一个 ProductsListViewModel 对象作为范数据传递让了视图。

 

此刻,视图期望之是一个 Product 对象的队列,因此用更新 List.cshtml
,以处理者新视图模型类型,修改后代码如下:

@model SportsStore.WebUI.Models.ProductsListViewModel

@{
    ViewBag.Title = "Products";
}

@foreach (var p in Model.Products) { 
<div class="item">
    <h3>@p.Name</h3>
    @p.Description
    <h4>@p.Price.ToString("c")</h4>
</div>
}

 

这个事例修改了 @model 指示符,以晓
Razor,现在在下一个不比之数据类型。也欲创新 foreach
循环,以要数据源是范数据的 Products 属性。

 

(4)显示页面链接

如今曾经就好了重新 List
视图上补偿加页面链接的具备准备。前面已经创办了带有分页信息之视图模型,更新了控制器以要之消息能够传递给视图,并修改了
@model 指示符以匹配新的视图模型类。剩下的转业是以视图中调用这个 HTML
辅助器方法,修改视图文件如下:

@model SportsStore.WebUI.Models.ProductsListViewModel

@{
    ViewBag.Title = "Products";
}

@foreach (var p in Model.Products)
{ 
    <div class="item">
        <h3>@p.Name</h3>
        @p.Description
        <h4>@p.Price.ToString("c")</h4>
    </div>
}

<div class="pager">
    @Html.PageLinks(Model.PagingInfo, x => Url.Action("List", new { page = x }));
</div>

运转该应用程序,可以见到都补给加了页面链接,如下图所展示。这个链接的体依旧是生基本。重要之是这个链接能拿客户从一个页面带顶外一个页面,并浏览着销售的产品。

SQL Server 15

5.2 改进 URL

页面链接虽然足其意图,但它运的仍然是询问字符串,以便将分页信息服务器,代码如下:

http://localhost/?page=2

一个重复好的办法是特别创建同种植照可结合 URL 模式之方案。“可组合 URL
”是一致栽对用户发生含义之章程,其款式如下:

http://localhost/Page2

侥幸的凡,MVC 很容易改 URL 方案,因为她以了 ASP.NET
的路由特性。所设开的学问吧一长新程由于补缺加到 Global.asax.cs 中的
RegisterRoutes 方法,如下所示:

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 }
            );

        }
    }
}

第一之是吧这条路由于加在 Default
路由之前。路由是仍其排有之各个进行拍卖的,这里用马上漫长新程由于预先让已经存在的那条。

立马是唯一要针对成品分页的 URL 方案展开改动的地方。MVC
框架和程由于功能是密切并的。因此这样的改动以自行反映在 Url.Action
方法的处理结果中。如果运行是应用程序,并导航及一个页面,将会见到是新的
URL 方案以该意图。

SQL Server 16

 

6.安情节样式

前面早已立了大气之功底结构,而且应用程序也起真的地围拢合在一起了,但绝非把注意力放到其外观及。即使这仍开不是相同如约关于
Web 设计或 CSS 的题,但SportsStore
应用程序设计也会见坐太糟糕之格式而破坏其的艺强度。本节以部分好端端的事体。

正文打算实现一个带有头部的经典式两排列布局。

 

6.1 定义布局中的公用内容

【MVC 4】2.使用
Razor 中都讲了
Razor 布局是安工作以及利用的。 当为 Product 控制器创建 List.cshtml
视图时,曾求用户选中 “使用一个布局”
复选框,但该文本框保留为空。这即以了默认布局 _Layout.cshtml,可以在
SportsStore.WebUI 项目的 Views/Shared
文件夹着找到它们。打开这个文件并修改如下:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width" />
    <title>@ViewBag.Title</title>
    <link href="~/Content/Site.css" type="text/css" rel="stylesheet" />
</head>
<body>
    <div id="header">
        <div class="title">SPORTS STORE</div>
    </div>
    <div id="categories">
        We will put something useful here later
    </div>
    <div id="content">
        @RenderBody()
    </div>
</body>
</html>

 

6.2 添加CSS样式

改引用的 CSS 样式文件 Site.css 如下:

...
body { font-family: Cambria, Georgia, "Times New Roman"; margin: 0; }
div#header div.title, div.item h3, div.item h4, div.pager a { font: bold 1em "Arial Narrow", "Franklin Gothic Medium", Arial; }
div#header { background-color: #444; border-bottom: 2px solid #111; color: white; }
div#header div.title { font-size: 2em; padding: .6em; }
div#content { border-left: 2px solid gray; margin-left: 9em; padding: 1em; }
div#categories { float: left; width: 8em; padding: .3em; }
div.item { border-top: 1px dotted gray; padding-top: .7em; margin-bottom: .7em; }
div.item:first-child { border-top: none; padding-top: 0; }
div.item h3 { font-size: 1.3em; margin: 0 0 .25em 0; }
div.item h4 { font-size: 1.1em; margin: .4em 0 0 0; }
div.pager { text-align: right; border-top: 2px solid silver; padding: .5em 0 0 0; margin-top: 1em; }
div.pager a { font-size: 1.1em; color: #666; text-decoration: none; padding: 0 .4em 0 .4em; }
div.pager a:hover { background-color: silver; }
div.pager a.selected { background-color: #353535; color: white; }
...

若运行程序,会看出该外观都获得改良,效果图如下:

SQL Server 17

 

6.3 创建分部视图

本文的结尾一个技巧是重构应用程序,以简化 List.cshtml
视图。本节打算创建一个分部视图(Partial
View)
,这种分部视图是放在其他一个视图中之一个内容片断。分部视图是打包含文件,且可以跨视图重用,这有助于减少重复,尤其是急需以应用程序的几只地方渲染同样的多寡时。

为丰富分部视图,右击 SportsStore.WebUI 项目遭到之 /Views/Shared
文件夹,添加新的视图文件 ProductSummary.cshtml 。

SQL Server 18

改视图文件如下:

@model SportsStore.Domain.Entities.Product

<div class="item">
    <h3>@Model.Name</h3>
    @Model.Description
    <h4>@Model.Price.ToString("c")</h4>
</div>

紧接着更新 Views/Product/List.cshtml ,以使她能使用这分部视图。

@model SportsStore.WebUI.Models.ProductsListViewModel

@{
    ViewBag.Title = "Products";
}

@foreach (var p in Model.Products)
{
    Html.RenderPartial("ProductSummary",p);
}

<div class="pager">
    @Html.PageLinks(Model.PagingInfo, x => Url.Action("List", new { page = x }))
</div>

这个事例已经失去丢了之前的 List.cshtml 视图中之 foreach
循环中的号子,并拿它们改变化了这新的分部视图中。用 Html.RenderPartial
辅助器方法来调用这个分部视图,参数是视图的称及视图模型对象。

提示:RenderPartial 方法并无像大多数别辅助器方法那样返回 HTML
标记。相反,它将内容一直写副到响应流,因此必须用一个分行,像一个完全的
C# 程序行一样来调动用其。这正如缓存已渲染之分部视图的 HTML
更实惠有,因为她以于描写到响应流。如果爱用同栽更平等的语法,可以使用
Html.partial 方法,它成功及 RenderPartial 方法同样的力量,但返回的是一个
HTML 片段,并且能如 @Html.Partial(“ProductSummary”,p) 一样用她。

运转应用程序,效果不变换。

相关文章