分享web开发知识

注册/登录|最近发布|今日推荐

主页 IT知识网页技术软件开发前端开发代码编程运营维护技术分享教程案例
当前位置:首页 > 前端开发

跟我学ASP.NET MVC之五:一个完整的应用程序 - SportsStrore

发布时间:2023-09-06 01:53责任编辑:顾先生关键词:.NETMVC

摘要:

这篇文章将介绍一个ASP.NET应用程序SportsStore的开发过程。

开始

创建解决方案

创建工程

在New ASP.NET Project - SportsStore窗口中,选择Empty模板和MVC folders。其他的模板将自动给你创建一些文件夹和文件,这里我选择Empty,从干净的工程里开始,演示如何将模板的东西加进来。

  创建后,将SportsStore工程改名为SportsStore.UI。

 创建另一个Class Library工程:SportsStore.Domain。

创建后的工程结构:

安装Package,注意版本冲突:

Ninject:

 Ninject.Web.Common:

   Ninject.MVC3:

安装完Package后的SportsStore.WebUI引用:

在工程SportsStore.WebUI中添加SportsStore.Domain的引用:

添加引用后:

设置DI容器

在SportsStore.WebUI工程中添加文件夹Infrastructure,在文件夹中添加代码文件NinjectDependencyResolver.cs。用来实例化DI容器定义的对象。

在我的博客文章:Ninject之旅之十三:Ninject在ASP.NET MVC程序上的应用(附程序下载)介绍了如何在ASP.NET项目中运用Ninject框架。

 1 using Ninject; 2 using System; 3 using System.Collections.Generic; 4 using System.Web.Mvc; 5 ?6 namespace SportsStore.WebUI.Infrastructure 7 { 8 ????public class NinjectDependencyResolver : IDependencyResolver 9 ????{10 ????????private IKernel kernel;11 ????????public NinjectDependencyResolver(IKernel kernelParam)12 ????????{13 ????????????kernel = kernelParam;14 ????????????AddBindings();15 ????????}16 ????????public object GetService(Type serviceType)17 ????????{18 ????????????return kernel.TryGet(serviceType);19 ????????}20 ????????public IEnumerable<object> GetServices(Type serviceType)21 ????????{22 ????????????return kernel.GetAll(serviceType);23 ????????}24 ????????private void AddBindings()25 ????????{26 ????????????// put bindings here27 ????????}28 ????}29 }

修改代码NinjectWebCommon.cs内的方法:RegisterServices。(安装Ninject Package时自动在文件夹App_Start中创建了这个代码文件),建立ASP.NET MVC应用程序和Ninject框架之间的桥梁。

1 ????????/// <summary>2 ????????/// Load your modules or register your services here!3 ????????/// </summary>4 ????????/// <param name="kernel">The kernel.</param>5 ????????private static void RegisterServices(IKernel kernel)6 ????????{7 ????????????System.Web.Mvc.DependencyResolver.SetResolver(new SportsStore.WebUI.Infrastructure.NinjectDependencyResolver(kernel));8 ????????} ??

加粗行的代码就是用于建立ASP.NET MVC应用程序和Ninject框架之间的桥梁。

至此,一个简单的ASP.NET MVC应用程序的框架部分完成了。

创建Domain Model

在工程SportsStore.Domain工程内创建文件夹Entities,在文件夹中创建代码文件Product.cs。

代码: 

 1 namespace SportsStore.Domain.Entities 2 { 3 ????public class Product 4 ????{ 5 ????????public int ProductID { get; set; } 6 ????????public string Name { get; set; } 7 ????????public string Description { get; set; } 8 ????????public decimal Price { get; set; } 9 ????????public string Category { get; set; }10 ????}11 }

Product类是一个简单实体类,注意类的访问属性改成了public。

创建抽象业务逻辑层

在SportsStore.Domain工程里,添加文件夹Abstract,用于放置抽象访问层的接口代码。

代码:

 1 using SportsStore.Domain.Entities; 2 using System.Collections.Generic; 3 ?4 namespace SportsStore.Domain.Abstract 5 { 6 ????public interface IProductRepository 7 ????{ 8 ????????IEnumerable<Product> Products { get; } 9 ????}10 }

 接口IProductRepository目前只定义了一个接口属性Products,用于枚举所有的产品集合。

定义接口,有利于使用Mock工具定义单元测试。

 创建数据库

 使用SQL Server数据库,创建数据库SportsStore。在数据库里创建表Products。

在Produts表里添加一些测试数据:

创建数据访问层

本文将使用EntityFramework框架作为数据访问层底层框架,首先为工程SportsStore.WebUI和工程SportsStore.Domain安装EntityFramework框架。

以及

 创建数据访问层基础代码

在SportsStore.Domain工程里添加文件夹Concrete,在文件夹中添加代码文件EFDbContext.cs。

EFDbContext.cs代码:

 1 using SportsStore.Domain.Entities; 2 using System.Data.Entity; 3 ?4 namespace SportsStore.Domain.Concrete 5 { 6 ????public class EFDbContext : DbContext 7 ????{ 8 ????????public DbSet<Product> Products { get; set; } 9 ????}10 }
EFDbContext类继承DbContext类,作为数据访问层容器。每一个表定义一个DbSet的泛型属性。
public DbSet<Product> Products { get; set; } 表示将Products属性映射到数据库的Products表,他的实体类是Product。

添加连接字符串:

修改SportsStore.WebUI工程根目录下的文件Web.config,添加connectionStrings节点:
1 ??<connectionStrings>2 ????<add name="EFDbContext" providerName="System.Data.SqlClient" connectionString="Data Source=localhost;Initial Catalog=SportsStore;Integrated Security=True" />3 ??</connectionStrings>

注意两点:

1. 根目录下的Web.config文件。

2. add name属性需要跟数据访问层的类类名相同,这里是EFDbContext。

3. connectionStrings节点必须在configSections节点的下方。或者说configSections必须是Web.config文件的第一个节点。

创建数据访问层代码

在Concrete文件夹中创建代码文件EFProductRepository.cs。

 1 using SportsStore.Domain.Abstract; 2 using SportsStore.Domain.Entities; 3 using System.Collections.Generic; 4 ?5 namespace SportsStore.Domain.Concrete 6 { 7 ????public class EFProductRepository : IProductRepository 8 ????{ 9 ????????private EFDbContext context = new EFDbContext();10 ????????public IEnumerable<Product> Products11 ????????{12 ????????????get13 ????????????{14 ????????????????try15 ????????????????{16 ????????????????????return context.Products;17 ????????????????}18 ????????????????catch (System.Exception e)19 ????????????????{20 ????????????????????return null;21 ????????????????}22 ????????????}23 ????????}24 ????}25 }
  • EFProductRepository类继承接口IProductRepository。
  • EFProductRepository类里包含了一个EFDbContext对象context,使用new关键字实例化context对象。
  • Products属性中,调用context.Products属性从数据库返回实体类Product的集合。

要使用这个repository类,我需要使用Ninject容器添加对这个类EFProductRepository的绑定。在类NinjectDependencyResolver中修改方法AddBindings,添加EFProductRepository绑定到接口EFProductRepository。

 1 using Ninject; 2 using SportsStore.Domain.Abstract; 3 using SportsStore.Domain.Concrete; 4 using System; 5 using System.Collections.Generic; 6 using System.Web.Mvc; 7 ?8 namespace SportsStore.WebUI.Infrastructure 9 {10 ????public class NinjectDependencyResolver : IDependencyResolver11 ????{12 ????????private IKernel kernel;13 ????????public NinjectDependencyResolver(IKernel kernelParam)14 ????????{15 ????????????kernel = kernelParam;16 ????????????AddBindings();17 ????????}18 ????????public object GetService(Type serviceType)19 ????????{20 ????????????return kernel.TryGet(serviceType);21 ????????}22 ????????public IEnumerable<object> GetServices(Type serviceType)23 ????????{24 ????????????return kernel.GetAll(serviceType);25 ????????}26 ????????private void AddBindings()27 ????????{28 ????????????kernel.Bind<IProductRepository>().To<EFProductRepository>();29 ????????}30 ????}31 }

添加ProductController

 1 using SportsStore.Domain.Abstract; 2 using System.Web.Mvc; 3 ?4 namespace SportsStore.WebUI.Controllers 5 { 6 ????public class ProductController : Controller 7 ????{ 8 ????????private IProductRepository repository; 9 10 ????????public ProductController(IProductRepository productRepository)11 ????????{12 ????????????this.repository = productRepository;13 ????????}14 15 ????????public ViewResult List()16 ????????{17 ????????????return View(repository.Products);18 ????????}19 ????}20 }
  • ProductController类中,定义一个IProductRepository接口对象,使用DI的构造函数方式实例化这个对象。
  • List视图方法调用接口的Products属性和View函数,返回ViewResult视图。

添加布局视图

在SportsStore.WebUI工程的Views文件夹里,添加文件夹Shared。在文件夹内创建文件_Layout.cshtml。

 1 <!DOCTYPE html> 2 ?3 <html> 4 <head> 5 ????<meta name="viewport" content="width=device-width" /> 6 ????<title>@ViewBag.Title</title> 7 </head> 8 <body> 9 ????<div>10 ????????@RenderBody()11 ????</div>12 </body>13 </html>

在Views文件夹的根目录下,添加_ViewStart.cshtml文件。

1 @{2 ????Layout = "~/Views/Shared/_Layout.cshtml";3 }

添加List视图:

 1 @using SportsStore.Domain.Entities 2 @model IEnumerable<Product> 3 @{ 4 ????ViewBag.Title = "Products"; 5 } ?6 ?7 @foreach (var p in Model) { 8 <div> 9 ????<h3>@p.Name</h3>10 ????@p.Description11 ????<h4>@p.Price.ToString("c")</h4>12 </div>13 }

@model IEnumerable<Product>指定该视图的数据模型是IEnumerable<Product>类型。

Model是一个Product集合,通过foreach访问这个集合。

p.Price.ToString("c")是按当前应用程序的文化信息格式化显示产品金额。可以修改Web.config文件的system.web节点下的globalization信息修改格式化信息。例如下面将修改成英镑格式:

<globalization culture="en-GB" uiCulture="en-GB" />

运行程序,访问url路径 /Product/List,得到运行结果:

 设置默认路由

上面运行结果,如果删除/Product/List,将得到404页面。

这时候,我们需要修改默认路由,网站访问默认访问路径/Product/List。

在文件夹App_Start下,找到代码文件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 RouteConfig11 ????{12 ????????public static void RegisterRoutes(RouteCollection routes)13 ????????{14 ????????????routes.IgnoreRoute("{resource}.axd/{*pathInfo}");15 16 ????????????routes.MapRoute(17 ????????????????name: "Default",18 ????????????????url: "{controller}/{action}/{id}",19 ????????????????defaults: new { controller = "Product", action = "List", id = UrlParameter.Optional }20 ????????????);21 ????????}22 ????}23 }

defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }

修改成

defaults: new { controller = "Product", action = "List", id = UrlParameter.Optional }

修改默认路由后,默认的访问路径就变成了Product/List。

再次运行程序,得到运行结果:

添加分页

现在在一个页面中显示了所有产品,但是在应用程序中如果产品数量比较多,一般要使用分页技术对列表进行分页。

首先需要添加视图模型类PageInfo。

为了支持HTML helper,我将向视图传递这些信息:页面数量、当前页数、总页数和产品数量。最简单的方法就是创建一个视图模型。

在工程SportsStore.WebUI里找到文件夹Models,在文件夹内添加代码文件PagingInfo.cs。

 1 using System; 2 ?3 namespace SportsStore.WebUI.Models 4 { 5 ????public class PagingInfo 6 ????{ 7 ????????public int TotalItems { get; set; } 8 ????????public int ItemsPerPage { get; set; } 9 ????????public int CurrentPage { get; set; }10 ????????public int TotalPages11 ????????{12 ????????????get13 ????????????{14 ????????????????return (int)Math.Ceiling((decimal)TotalItems / ItemsPerPage);15 ????????????}16 ????????}17 ????}18 }

这也是一个简单类,只包含了模型属性,不包含方法。

有了分页模型类,还需要定义一个包含产品列表和分页信息对象的模型类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 ????}11 }

创建后的文件:

 有了视图模型类,现在需要修改ProductController,使用这两个模型类。

 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(int page = 1)20 ????????{21 ????????????ProductsListViewModel model = new ProductsListViewModel22  ???????????{23  ???????????????Products = repository.Products.OrderBy(p => p.ProductID).Skip((page - 1) * PageSize).Take(PageSize),24 ???? ???????????PagingInfo = new PagingInfo25  ????????????{26 ? ??????????????????CurrentPage = page,27 ????????????????????ItemsPerPage = PageSize,28  ???????????????????TotalItems = repository.Products.Count()29  ????????????}30  ?????????};31  ???????????return View(model);32 ????????}33 ????}34 }
  • using SportsStore.WebUI.Models;:使用视图模型类,需要添加引用
  • public int PageSize = 4;:定义分页记录数为4。
  • List(int page = 1):List方法默认参数是1,如果访问url:Product/List,将默认返回第一页产品。
  • using System.Linq;:分页调用了Linq的扩展方法,所以需要添加对Linq的引用。
  • ProductsListViewModel model = new ProductsListViewModel:生成视图模型对象,包含Products对象和PagingInfo对象。
  • Products = repository.Products.OrderBy(p => p.ProductID).Skip((page - 1) * PageSize).Take(PageSize):获得本页中的Product列表。
  • return View(model);:将新的视图模型返回至视图。

下面需要创建帮助类,生成分页HTML元素。

创建文件夹HtmlHelpers,在文件夹内创建代码文件PagingHelpers.cs。

 1 using SportsStore.WebUI.Models; 2 using System; 3 using System.Text; 4 using System.Web.Mvc; 5 ?6 namespace SportsStore.WebUI.HtmlHelpers 7 { 8 ????public static class PagingHelpers 9 ????{10 ????????public static MvcHtmlString PageLinks(this HtmlHelper html, PagingInfo pagingInfo, Func<int, string> pageUrl)11 ????????{12 ????????????StringBuilder result = new StringBuilder();13 ????????????for (int i = 1; i <= pagingInfo.TotalPages; i++)14 ????????????{15 ????????????????TagBuilder tag = new TagBuilder("a");16 ????????????????tag.MergeAttribute("href", pageUrl(i));17 ????????????????tag.InnerHtml = i.ToString();18 ????????????????if (i == pagingInfo.CurrentPage)19 ????????????????{20 ????????????????????tag.AddCssClass("selected");21 ????????????????????tag.AddCssClass("btn-primary");22 ????????????????}23 ????????????????tag.AddCssClass("btn btn-default");24 ????????????????result.Append(tag.ToString());25 ????????????}26 ????????????return MvcHtmlString.Create(result.ToString());27 ????????}28 ????}29 }
  • PageLinks扩展方法使用PagingInfo对象的信息生成HTML的分页链接。
  • Func参数接受一个用于向视图生成链接的代理方法。
  • 这里还利用了TagBuilder类,调用ToString方法生成HTML的链接字符串。

有个这个扩展方法后,还需要在视图文件夹Views里的web.config文件中对定义这个方法的所在类进行声明,声明后的视图才能够使用这个扩展方法。

在Views文件夹内找到文件web.config。

找到节点system.web.webPages.razor,在namespaces里添加:<add namespace="SportsStore.WebUI.HtmlHelpers"/>。

最后是修改List.cshtml视图文件。

 1 @model SportsStore.WebUI.Models.ProductsListViewModel 2 ?3 @{ 4 ????ViewBag.Title = "Products"; 5 } ?6 ?7 @foreach (var p in Model.Products) { 8 <div> 9 ????<h3>@p.Name</h3>10 ????@p.Description11 ????<h4>@p.Price.ToString("c")</h4>12 </div>13 }14 <div>15 @Html.PageLinks(Model.PagingInfo, x => Url.Action("List", new { page = x }))16 </div>
  • @model SportsStore.WebUI.Models.ProductsListViewModel:修改视图声明使用新的视图模型。
  • Model.Products:Model代表了新的ProductListViewModel类型对象,通过他获得Products列表
  • @Html.PageLinks(Model.PagingInfo, x => Url.Action("List", new { page = x })):调用Html的扩展方法PageLinks,获得分页的HTML字符串,显示到页面上。第一个参数是Model.PagingInfo,第二个参数是一个Func委托,委托使用Url.Action方法生成超链接。

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

第一页:

第二页:

第三页:

改进分页链接

 现在的分页url是这样的:http://localhost:17596/?page=2,需要传入一个request参数。这样可读性不强。

可以将分页的url改成:http://localhost:17596/page2,这样可读性更好。

需要修改路由信息达到这个目的。

还是找到文件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 RouteConfig11 ????{12 ????????public static void RegisterRoutes(RouteCollection routes)13 ????????{14 ????????????routes.IgnoreRoute("{resource}.axd/{*pathInfo}");15 16  ???????????routes.MapRoute(17  ???????????????name: null,18 ??? ????????????url: "Page{page}",19 ??? ????????????defaults: new { Controller = "Product", action = "List" }20  ????????);21 22 ????????????routes.MapRoute(23 ????????????????name: "Default",24 ????????????????url: "{controller}/{action}/{id}",25 ????????????????defaults: new { controller = "Product", action = "List", id = UrlParameter.Optional }26 ????????????);27 ????????}28 ????}29 }
  • routes方法MapRoute在路由表对象routes里的首部插入新的路由信息,路由将从上往下匹配url,将找到的第一个路由信息返回,生成url字符串,然后结束查找。
  • 这里定义新的路由url是:Page{page},{page}就是传入action方法的参数名。

运行程序,得到运行结果,注意生成的链接指向的url变成了Page{page}。

翻到第二页:

第三页:

 为应用程序添加样式

 到目前为止,这个应用程序没有应用任何样式。我将使用BootStrap作为视图的样式表。

BootStrap是Twitter公司在2012年开发的一个前端样式表框架,现在已经广泛使用在web应用程序中。

添加BootStrap的package。

安装后展开Content文件夹,自动给应用程序添加了bootstrap样式表。fonts文件夹下添加了bootstrap字体,Scripts文件夹下添加了bootstrap的JavaScript文件。

修改_Layout.cshtml文件,应用bootstrap。

 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 id="categories" class="col-xs-3">16 ????????????Put something useful here later17 ????????</div>18 ????????<div class="col-xs-8">19 ????????????@RenderBody()20 ????????</div>21 ????</div>22 </body>23 </html>

修改List.cshtml文件,应用bootstrap。

 1 @model SportsStore.WebUI.Models.ProductsListViewModel 2 ?3 @{ 4 ????ViewBag.Title = "Products"; 5 } ?6 ?7 @foreach (var p in Model.Products) 8 { 9 ????<div class="well">10 ????????<h3>11 ????????????<strong>@p.Name</strong>12 ????????????<span class="pull-right label labelprimary">@p.Price.ToString("c")</span>13 ????????</h3>14 ????????<span class="lead"> @p.Description</span>15 ????</div>16 }17 <div>18 @Html.PageLinks(Model.PagingInfo, x => Url.Action("List", new { page = x }))19 </div>

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

跟我学ASP.NET MVC之五:一个完整的应用程序 - SportsStrore

原文地址:https://www.cnblogs.com/uncle_danny/p/9029386.html

知识推荐

我的编程学习网——分享web前端后端开发技术知识。 垃圾信息处理邮箱 tousu563@163.com 网站地图
icp备案号 闽ICP备2023006418号-8 不良信息举报平台 互联网安全管理备案 Copyright 2023 www.wodecom.cn All Rights Reserved