[.NET] 一步步打造一个简单的 MVC 电商网站 - BooksStore(一)

news/2024/5/20 15:47:32/文章来源:https://blog.csdn.net/weixin_34252686/article/details/86396066

一步步打造一个简单的 MVC 电商网站 - BooksStore(一)

  本系列的 GitHub地址:https://github.com/liqingwen2015/Wen.BooksStore

  《一步步打造一个简单的 MVC 电商网站 - BooksStore(一)》(发布时间:2017-03-30 )

      《一步步打造一个简单的 MVC 电商网站 - BooksStore(二)》(发布时间:2017-03-31)

  《一步步打造一个简单的 MVC 电商网站 - BooksStore(三)》(发布时间:2017-04-01)

  《一步步打造一个简单的 MVC 电商网站 - BooksStore(四)》(发布时间:2017-04-05)

 

简介

  主要功能与知识点如下:

    分类、产品浏览、购物车、结算、CRUD(增删改查) 管理、发邮件、分页、模型绑定、认证过滤器和单元测试等(预计四篇、周五、下周一和周二)。

     【备注】项目使用 VS2015 + C#6 进行开发,有问题请发表在留言区哦,还有,页面长得比较丑,请见谅。

 

目录

  • 创建项目架构
  • 创建域模型实体
  • 创建单元测试
  • 创建控制器与视图
  • 创建分页
  • 加入样式

 

一、创建项目架构

  1.新建一个解决方案“BooksStore”,并添加以下项目:

  

BooksStore.Domain:类库,存放域模型和逻辑,使用 EF;
BooksStore.WebUI:Web MVC 应用程序,存放视图和控制器,充当显示层,使用了 Ninject 作为 DI 容器;
BoosStore.UnitTest:单元测试,对上述两个项目进行测试。

  

  Web MVC 为一个空的 MVC 项目:

 

  2.添加项目引用(需要使用 NuGet):

    

  这是不同项目需要引用的类库和项目

 

3.设置 DI 容器
我们通过 Ninject ,创建一个自定义的工厂,一个名为 NinjectControllerFactory 的类继承 DefaultControllerFactory(默认的控制器工厂)。你也可以在里面添加自定义的代码,改变 MVC 框架的默认行为。

   AddBindings() 添加绑定方法,先留空。

    public class NinjectControllerFactory : DefaultControllerFactory{private readonly IKernel _kernel;public NinjectControllerFactory(){_kernel = new StandardKernel();AddBindings();}protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType){return controllerType == null? null: (IController) _kernel.Get(controllerType);}/// <summary>/// 添加绑定/// </summary>private void AddBindings(){}}

 

4.并且在 Global.asax 中加入一行代码,告诉 MVC 用新建的类来创建控制器对象。
ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory());
    public class MvcApplication : System.Web.HttpApplication{protected void Application_Start(){AreaRegistration.RegisterAllAreas();RouteConfig.RegisterRoutes(RouteTable.Routes);ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory());}}
Global.asax

 

二、创建域模型实体

1.在图中位置创建一个名为 Book 的实体类。
    public class Book{/// <summary>/// 标识/// </summary>public int Id { get; set; }/// <summary>/// 名称/// </summary>public string Name { get; set; }/// <summary>/// 描述/// </summary>public string Description { get; set; }/// <summary>/// 价格/// </summary>public decimal Price { get; set; }/// <summary>/// 分类/// </summary>public string Category { get; set; }}

     

  有了实体之后,我们应该创建一个“库”对该实体进行操作,而这种持久化逻辑操作也应该和域模型是进行隔离的。

  2.先定义一个接口 IbookRepository,在根目录创建一个名为 Abstract 的文件夹,顾名思义就是应该放置一些抽象的类,如接口。

    public interface IBookRepository{IQueryable<Book> Books { get; }}

  我们通过该接口就可以得到对应类的相关信息,而不需要去管该数据如何存储,以及存储的位置,这就是存储库模式的本质。

 

  3.接下来,我们就需要对数据库进行操作了,我们使用简单的 EF(ORM 对象关系模型) 去对数据库进行操作,所以需要自己通过 Nuget 下载 EF 的类库。

 

  4.因为之前定义了接口类,接下来就应该定义实现该接口的类了

 

  安装完之后,再次建立一个名为 Concrete 的文件夹,存放实例。

  在里面创建一个 EfDbContext 的类,派生于 DbContext,该类会为用户要使用的数据库中的每个表自动的定义一个属性。该属性名为 Books,指定了表名,DbSet<Book> 表示为 Book 实体的表模型,Book 对象相当于 Books 表中的行(记录)。

    public class EfDbContext : DbContext{public DbSet<Book> Books { get; set; }}

 

  再创建一个 EfBookRepository 存储库类,它实现 IBookRepository 接口,使用了上文创建的 EfDbContext 上下文对象,包含了具体的方法定义。

    public class EfBookRepository : IBookRepository{private readonly EfDbContext _context = new EfDbContext();public IQueryable<Book> Books => _context.Books;} 

 

   5.现在只差在数据库新建一张表了。

CREATE TABLE Book
(Id INT IDENTITY PRIMARY KEY,Name NVARCHAR(100),Description NVARCHAR(MAX),Price DECIMAL,Category NVARCHAR(50)
)

   并插入测试数据:

INSERT INTO dbo.Book( Name ,Description ,Price ,Category)
VALUES  ( N'C#从入门到精通' , -- Name - nvarchar(100)N'好书-C#从入门到精通' , -- Description - nvarchar(max)50 , -- Price - decimalN'.NET'  -- Category - nvarchar(50)
        )INSERT INTO dbo.Book( Name ,Description ,Price ,Category)
VALUES  ( N'ASP.NET从入门到精通' , -- Name - nvarchar(100)N'好书-ASP.NET从入门到精通' , -- Description - nvarchar(max)60 , -- Price - decimalN'.NET'  -- Category - nvarchar(50)
        )INSERT INTO dbo.Book( Name ,Description ,Price ,Category)
VALUES  ( N'多线程从入门到精通' , -- Name - nvarchar(100)N'好书-多线程从入门到精通' , -- Description - nvarchar(max)65 , -- Price - decimalN'.NET'  -- Category - nvarchar(50)
        )INSERT INTO dbo.Book( Name ,Description ,Price ,Category)
VALUES  ( N'java从入门到放弃' , -- Name - nvarchar(100)N'好书-java从入门到放弃' , -- Description - nvarchar(max)65 , -- Price - decimalN'java'  -- Category - nvarchar(50)
        )INSERT INTO dbo.Book( Name ,Description ,Price ,Category)
VALUES  ( N'sql从入门到放弃' , -- Name - nvarchar(100)N'好书-sql从入门到放弃' , -- Description - nvarchar(max)45 , -- Price - decimalN'sql'  -- Category - nvarchar(50)
        )INSERT INTO dbo.Book( Name ,Description ,Price ,Category)
VALUES  ( N'sql从入门到出家' , -- Name - nvarchar(100)N'好书-sql从入门到出家' , -- Description - nvarchar(max)45 , -- Price - decimalN'sql'  -- Category - nvarchar(50)
        )INSERT INTO dbo.Book( Name ,Description ,Price ,Category)
VALUES  ( N'php从入门到出家' , -- Name - nvarchar(100)N'好书-php从入门到出家' , -- Description - nvarchar(max)45 , -- Price - decimalN'php'  -- Category - nvarchar(50))
测试数据

 

  因为我希望表名为 Book,而不是 Books,所以我在之前的 Book 类上加上特性 [Table("Book")] : 

 

三、创建单元测试

  1.做完预热操作后,你可能想立即以界面的的方式进行显示,别急,先用单元测试检查一下我们对数据库的操作是否正常,通过对数据进行简单的读取,检查下连接是否成功。

  

  2.单元测试也需要引入 ef 类库(Nuget)。

  3.安装完之后会生成一个 app.config 配置文件,需要额外添加一行连接字符串(在后续的 Web UI 项目里,也需要加上这条信息,不然会提示对应的错误信息)。

  <connectionStrings><add name="EfDbContext" connectionString="server=.;database=TestDb;uid=sa;pwd=123" providerName="System.Data.SqlClient"/></connectionStrings>

 

   4.当所有前置工作都准备好了的时候,就应该填写测试方法了,因为我插入了 7 条数据,这里我就判断一下从数据库读取出的行数是否为 7 :

        [TestMethod]public void BooksCountTest(){var bookRepository=new EfBookRepository();var books = bookRepository.Books;Assert.AreEqual(books.Count(),7);}

  

  5.在该方法体的内部单击右键,你可以看到一个“运行测试”的选项,这时你可以尝试单击它:

  从这个符号可以看到,是成功了!

  接下来,我们要正式从页面显示我们想要的信息了。

 

四、创建控制器与视图 

  1.先新建一个空的控制器:BookController:

 

  2.需要我们自定义一个 Details 方法,用于后续与界面进行交互。

    public class BookController : Controller{private readonly IBookRepository _bookRepository;public BookController(IBookRepository bookRepository){_bookRepository = bookRepository;}/// <summary>/// 详情/// </summary>/// <returns></returns>public ActionResult Details(){return View(_bookRepository.Books);}}

 

  3.接下来,要创建一个视图 View 了。

 

  4.将 Details.cshtml 的内容替换为下面的:

@model IEnumerable<Wen.BooksStore.Domain.Entities.Book>@{ViewBag.Title = "Books";
}@foreach (var item in Model)
{<div><h3>@item.Name</h3>@item.Description<h4>@item.Price.ToString("C")</h4><br /><hr /></div>
}

 

  5.改下默认的路由机制,让他默认跳转到该页面。

  

  6.还有一点需要注意的是,因为我们使用了 Ninject 容器,并且需要对控制器中的构造函数中的参数 IBookRepository 进行解析,告诉他将使用哪个对象对该接口进行服务,也就是需要修改之前的 AddBindings 方法:

 

  7.运行的效果大致如下(因为加了点 CSS 样式,所以显示的效果可能有些许不同),结果是一致的。

 

五、创建分页

  1.在 Models 文件夹新增一个 PagingInfo.cs 分页信息类。

    /// <summary>/// 分页信息/// </summary>public class PagingInfo{/// <summary>/// 总数/// </summary>public int TotalItems { get; set; }/// <summary>/// 页容量/// </summary>public int PageSize { get; set; }/// <summary>/// 当前页/// </summary>public int PageIndex { get; set; }/// <summary>/// 总页数/// </summary>public int TotalPages => (int)Math.Ceiling((decimal)TotalItems / PageSize);}

 

  2.新增一个 HtmlHelpers 文件夹存放一个基于 Html 帮助类的扩展方法:

    public static class PagingHelper{/// <summary>/// 分页/// </summary>/// <param name="helper"></param>/// <param name="pagingInfo"></param>/// <param name="func"></param>/// <returns></returns>public static MvcHtmlString PageLinks(this HtmlHelper helper, PagingInfo pagingInfo, Func<int, string> func){var sb = new StringBuilder();for (var i = 1; i <= pagingInfo.TotalPages; i++){//创建 <a> 标签var tagBuilder = new TagBuilder("a");//添加特性tagBuilder.MergeAttribute("href", func(i));//添加值tagBuilder.InnerHtml = i.ToString();if (i == pagingInfo.PageIndex){tagBuilder.AddCssClass("selected");}sb.Append(tagBuilder);}return MvcHtmlString.Create(sb.ToString());}}

 

   3.添加完毕后需要在配置文件内加入该命名空间

 

  4.现在要重新修改 BookController.cs 控制器内的的代码,并添加新的视图模型类 BookDetailsViewModels.cs,让它继承之前的分页类。

    public class BookDetailsViewModels : PagingInfo{public IEnumerable<Book> Books { get; set; }}

  修改后的控制器代码:

    public class BookController : Controller{private readonly IBookRepository _bookRepository;public int PageSize = 5;public BookController(IBookRepository bookRepository){_bookRepository = bookRepository;}/// <summary>/// 详情/// </summary>/// <param name="pageIndex"></param>/// <returns></returns>public ActionResult Details(int pageIndex = 1){var model = new BookDetailsViewModels(){Books = _bookRepository.Books.OrderBy(x => x.Id).Skip((pageIndex - 1) * PageSize).Take(PageSize),PageSize = PageSize,PageIndex = pageIndex,TotalItems = _bookRepository.Books.Count()};return View(model);}}

 

  5.修改视图模型后,对应的视图页也需要修改

@model Wen.BooksStore.WebUI.Models.BookDetailsViewModels@{ViewBag.Title = "Books";
}@foreach (var item in Model.Books)
{<div><h3>@item.Name</h3>@item.Description<h4>@item.Price.ToString("C")</h4><br /><hr /></div>
}<div class="pager">@Html.PageLinks(Model, x => Url.Action("Details", new { pageIndex = x }))
</div>

 

六、加入样式

  1.页面的样式简单的设计为 3 大板块,顶部为标题,左侧边栏为分类,主模块将显示具体内容。

  我们现在要在 Views 文件夹下创建一个文件 _ViewStart.cshtml,再创建一个 Shared 的文件夹和文件 _Layout.cshtml。

 

  2._Layout.cshtml 这是布局页,当代码执行到 @RenderBody() 时,就会负责将之前 Details.cshtml 的内容进行渲染:

<!DOCTYPE html><html>
<head><meta name="viewport" content="width=device-width" /><title>@ViewBag.Title</title><link href="~/Contents/Site.css" rel="stylesheet" />
</head>
<body><div id="header"><div class="title">图书商城</div></div><div id="sideBar">分类</div><div id="content">@RenderBody()</div>
</body>
</html>

   _ViewStart.cshtml 该文件表示默认的布局页为该视图文件:

@{Layout = "~/Views/Shared/_Layout.cshtml";
}

 

   3.网站的根目录下也要添加一个名为 Contents 的文件夹,用于存放 CSS。

body {
}#header, #content, #sideBar {display: block;
}#header {background-color: green;border-bottom: 2px solid #111;color: White;
}#header, .title {font-size: 1.5em;padding: .5em;
}#sideBar {float: left;width: 8em;padding: .3em;
}#content {border-left: 2px solid gray;margin-left: 10em;padding: 1em;
}.pager {text-align: right;padding: .5em 0 0 0;margin-top: 1em;
}.pager A {font-size: 1.1em;color: #666;padding: 0 .4em 0 .4em;}.pager A:hover {background-color: Silver;}.pager A.selected {background-color: #353535;color: White;}
Site.css

 

  现在,分页也已经有了效果,基本界面就出来了。

   本系列的 GitHub地址:https://github.com/liqingwen2015/Wen.BooksStore

 

错误修正

  感谢 韩之一 :BooksCountTest() 这个方法里 Assert.AreEqual(books.Count(), 7); 参数写反了, 第一个是期望值, 第二个是真实值。

 

 


【博主】反骨仔

【原文】http://www.cnblogs.com/liqingwen/p/6640861.html

【参考】《精通 ASP.NET MVC ...》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_773227.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

服务器打开_服务器网站打开速度慢怎样解决?

很多站长的网站建立起来之后&#xff0c;发现访问缓慢&#xff0c;有时候甚至要用十几秒才能打开&#xff0c;这无疑会影响网站的用户体验&#xff0c;还会影响网站的SEO优化&#xff0c;那么如何提升网站的打开速度呢&#xff1f;腾佑科技小编这里结合自己的建站经验教大家几招…

jpa jql 时间范围查询_SpringBoot入门建站全系列(五)使用Spring-data-jpa操作数据库...

SpringBoot入门建站全系列&#xff08;五&#xff09;使用Spring-data-jpa操作数据库SpringBoot操作数据库有多种方式&#xff0c;如JDBC直接操作:太古老了&#xff0c;没人愿意这样玩Mybatis插件&#xff1a;比较时髦&#xff0c;比较适合sql复杂&#xff0c;或者对性能要求高…

editor 插入图片之后将光标放到右侧_8个字,9张图教会你用PPT制作gif | 附赠3个gif图片网站...

首图送大家一个gif今儿想来和大家讲讲怎么用PPT做出你想要的gif动图&#xff0c;没错&#xff0c;就是PPT。简单来说&#xff0c;其实就是8个字&#xff1a;构思、作图、动画、录屏。就以这个首图为例吧方法一 < 构思 >知道自己想做什么风格的图&#xff0c;要起到什么作…

java与seo_学习seo和学习java哪个更好

SEO一个seo行业站点&#xff0c;科学的内容制作应该与seo相关&#xff0c;且内容本身是有人搜索的。seo和java哪个更好是有人搜索的语句&#xff0c;且与seo是强相关的&#xff0c;对于seo教程自学网来讲&#xff0c;这样的内容再适合不过了。任何一门技术&#xff0c;如果精通…

打开特定的网站特别慢_[独立站运营] 网站用户体验 — 可能是Google SEO最重要的部分...

前言&#xff1a;做独立站运营&#xff0c;SEO优化的重要性不言而喻。而做跨境电商独立站运营&#xff0c;更离不开SEO优化&#xff0c;尤其是针对Google搜索引擎。正文&#xff1a;Google拥有40多亿的网络用户&#xff0c;每天将近50亿的搜索量&#xff0c;每秒63000搜索结果显…

通过url账号密码登录其他网站_使用pyppeteer淘宝登录

使用pyppeteer淘宝登录&#xff0c;获取Cookies现在淘宝的商品搜索页必须要登录才能见&#xff0c;所以必须要cookies才能进行下一步操作。本期介绍如何使用pyppeteer登录淘宝&#xff0c;获取Cookies。pyppeteer介绍地址&#xff1a;https://github.com/miyakogi/pyppeteer介绍…

在线小说网站的设计与实现(附源码)

2019独角兽企业重金招聘Python工程师标准>>> 最近在做一个课程设计&#xff0c;在线小说网站的设计&#xff0c;以下是课题要求&#xff0c;需要项目练手的童鞋可以试试身手。 由于最近新学了JavaEE&#xff0c;所以采用了jspservlet来写&#xff0c;前端部分用了少…

ai png转矢量图_有哪些免费下载png图片的网站?

为了减少广大设计朋友们的工作量&#xff0c;Fotor懒设计给大家推荐9个免扣素材网站&#xff01;重要的是免费&#xff01;免费&#xff01;免费&#xff01;直接上干货&#xff01;01.PNGimg它还提供各种分类&#xff0c;有动物、电影、人物、电子产品、食物等等&#xff0c;需…

暂停服务器网站,服务器重启 iis网站暂停

服务器重启 iis网站暂停 内容精选换一换访问IIS搭建的网站不通&#xff0c;报错404。IIS上绑定的域名只填写了主机名&#xff0c;没有指定IP地址。本节操作指导用户修改IIS上绑定的域名&#xff0c;以Windows Server 2008 R2操作系统云服务器为例。登录服务器&#xff0c;选择“…

一个很有特色的网站,有些屏保/Applets,以及一个好用的Unlocker

Update: 按图索骥找到了这个站点&#xff1a;http://www.softpedia.com眼花缭乱&#xff0c;软件全的像猪头一样-----------------------------------------来自&#xff1a;http://ccollomb.free.fr/事实上我从这里找到的&#xff1a;http://yackol.com/blog/ &#xff0c;这是…

Castle开发系列文章上了Castle的官方网站

最近有些忙&#xff0c;Blog更新的速度明显有些慢了&#xff0c;也是很久没有上Castle的官方网站了&#xff0c;今天Terry82&#xff08;跟我名字有点像&#xff09;的朋友告诉我Castle 开发系列文章在Castle官方网站上有链接&#xff0c;上去看了一下&#xff0c;还真是&#…

C# 代码生成器 网站架构设计

自己写的一个 web 版简易 C# Code Generator&#xff0c;可快速产生某个数据库中&#xff0c;所有表 Mapping 的 C# 3.0 类。可当作网站分层开发、表单大量传递用户输入值、在内存里持久化保存值之用&#xff0c;亦可当作 NHibernate 等 O/R Mapping 框架套用时的 C# 代码生成器…

读读书,走走路——【读书】SEO智慧——搜索引擎优化与网站营销革命

首先是应该和大家道个歉&#xff0c;这已经很久没有更新了吧&#xff0c;希望大家原谅&#xff01;别人保研的日子各种轻松&#xff0c;我却各种忙碌&#xff0c;也许是想多做点事情&#xff0c;也许是心太急了点&#xff0c;也许是不想辜负老师的期望&#xff0c;总是找不到自…

python之_「svd」python之SVD函数介绍 - seo实验室

svd 详情&#xff1a;https://www.cnblogs.com/denny402/p/5096491.html我们看到一开始随机生成的数组与使用mat函数之后的类型是发生了变化的&#xff0c;尽管他们显示的东西没有什么区别&#xff0c;但是实质上&#xff0c;他们的类型是不同的。调用mat()函数可以将数组转换为…

winform怎么向网站发送json_JSON相关漏洞(Hijacking+Injection)挖掘技巧及实战案例全汇总...

本文一是在为测试过程中遇到json返回格式时提供测试思路&#xff0c;二是几乎所有国内的资料都混淆了json和jsonp的区别——这是两种技术&#xff1b;以及json和jsonp hijacking的区别——这是两个漏洞&#xff0c;这里做个解释。1、概念1)什么是jsonjson(JavaScript Object No…

编程要趁早 BI推荐8个编程学习网站

最近一个热门话题是雅虎以3000万美元收购了移动阅读应用Summly&#xff0c;真正让人吃惊的不是3000万美元的价格值不值&#xff0c;而是Summly的开发者--只有17岁的尼克达洛伊西奥。他12岁开始学习编程&#xff0c;2011年&#xff08;15岁时&#xff09;因Summly从李嘉诚基金获…

Java系的大网站架构-LinkedIn和淘宝

用Java的大网站很多&#xff08;LinkedIn&#xff0c;淘宝&#xff09;&#xff0c;当然用LAMP&#xff08;PHPMySQL&#xff09;的更多&#xff08;Facebook是把PHP转成C编译的&#xff0c;也算&#xff09;&#xff0c;用ASP.NET也有一些&#xff08;如Stackoverflow&#xf…

在github创建个人网站

2019独角兽企业重金招聘Python工程师标准>>> 详细见http://xieweizhong.tk/blog/ At first, I tried jeklly to create the static website on the github, but I found that it was difficult for me. Thus, I google the other way to create static website on g…

如何成功打造一个能够赚钱的个人博客网站?

2019独角兽企业重金招聘Python工程师标准>>> 今天的天气格外开朗&#xff0c;心情也自然好了很多&#xff0c;所以一到公司做下来&#xff0c;充满了激情和斗志。工作要继续做&#xff0c;日记要坚持写。由于最近一段时间&#xff0c;笔者发现国内一下子出来了大批的…

JavaScript网站设计实践(五)编写photos.html页面,实现点击缩略图显示大图的效果...

一、photos.html页面&#xff0c;点击每一张缩略图&#xff0c;就在占位符的位置那里&#xff0c;显示对应的大图。 看到的页面效果是这样的&#xff1a; 1、实现思路 这个功能在之前的JavaScript美术馆那里已经实现了。 首先在页面中使用ul列表显示出所有的缩略图&#xff0c;…