当前位置: 移动技术网 > IT编程>开发语言>.net > 一步步打造简单的MVC电商网站BooksStore(1)

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

2017年12月08日  | 移动技术网IT编程  | 我要评论

陈学冬出席米兰时装周,国庆文章,guodexiu

一步步打造一个简单的 mvc 电商网站 - booksstore(一)

本系列的 github地址:https://github.com/liqingwen2015/wen.booksstore

一步步打造一个简单的 mvc 电商网站 - booksstore(一)

一步步打造一个简单的 mvc 电商网站 - booksstore(二)

一步步打造一个简单的 mvc 电商网站 - booksstore(三)

一步步打造一个简单的 mvc 电商网站 - booksstore(四)

简介

主要功能与知识点如下:

分类、产品浏览、购物车、结算、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());
 }
 }

二、创建域模型实体

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)
, -- 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)
, -- price - decimal
  n'.net' -- category - nvarchar(50)
 )

insert into dbo.book
 ( 
  name ,
  description ,
  price ,
  category
 )
values ( 
  n'多线程从入门到精通' , -- name - nvarchar(100)
  n'好书-多线程从入门到精通' , -- description - nvarchar(max)
, -- 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)
, -- 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)
, -- 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)
, -- 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)
, -- price - decimal
  n'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;
 }

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

本系列的 github地址:https://github.com/liqingwen2015/wen.booksstore

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持移动技术网。

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网