纵有疾风起
人生不言弃

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

一步步打造一个简单的 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”,并添加以下项目:

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

  

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

  

  Web MVC 为一个空的 MVC 项目:

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

 

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

[.NET] 一步步打造一个简单的 MVC 电商网站 – BooksStore(一)插图2  [.NET] 一步步打造一个简单的 MVC 电商网站 – BooksStore(一)插图3  [.NET] 一步步打造一个简单的 MVC 电商网站 – BooksStore(一)插图4

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

 

  3.设置 DI 容器
     我们通过 Ninject ,创建一个自定义的工厂,一个名为 NinjectControllerFactory 的类继承 DefaultControllerFactory(默认的控制器工厂)。你也可以在里面添加自定义的代码,改变 MVC 框架的默认行为。
 [.NET] 一步步打造一个简单的 MVC 电商网站 – BooksStore(一)插图5

   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

 

二、创建域模型实体

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

     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 的文件夹,顾名思义就是应该放置一些抽象的类,如接口。

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

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

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

 

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

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

 

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

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

 

  安装完之后,再次建立一个名为 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 - decimal          N'.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 - decimal          N'.NET'  -- Category - nvarchar(50)        )INSERT INTO dbo.Book        (           Name ,          Description ,          Price ,          Category        )VALUES  (           N'多线程从入门到精通' , -- Name - nvarchar(100)          N'好书-多线程从入门到精通' , -- Description - nvarchar(max)          65 , -- Price - decimal          N'.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 - decimal          N'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 - decimal          N'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 - decimal          N'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 - decimal          N'php'  -- Category - nvarchar(50)        )

测试数据

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

 

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

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

 

三、创建单元测试

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

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

  

  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.在该方法体的内部单击右键,你可以看到一个“运行测试”的选项,这时你可以尝试单击它:

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

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

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

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

 

四、创建控制器与视图 

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

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

 

  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 了。

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

 

  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.改下默认的路由机制,让他默认跳转到该页面。

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

  

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

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

 

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

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

 

五、创建分页

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

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

    /// <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 帮助类的扩展方法:

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

    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.添加完毕后需要在配置文件内加入该命名空间

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

 

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

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

    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。
[.NET] 一步步打造一个简单的 MVC 电商网站 – BooksStore(一)插图26

 

  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

 

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

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

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

 

错误修正

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

 

 


【博主】反骨仔

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

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

文章转载于:https://www.cnblogs.com/liqingwen/p/6640861.html

原著是一个有趣的人,若有侵权,请通知删除

未经允许不得转载:起风网 » [.NET] 一步步打造一个简单的 MVC 电商网站 – BooksStore(一)
分享到: 生成海报

评论 抢沙发

评论前必须登录!

立即登录