一步步打造簡(jiǎn)單的MVC電商網(wǎng)站BooksStore(1)
一步步打造一個(gè)簡(jiǎn)單的 MVC 電商網(wǎng)站 - BooksStore(一)
本系列的 GitHub地址:https://github.com/liqingwen2015/Wen.BooksStore
《一步步打造一個(gè)簡(jiǎn)單的 MVC 電商網(wǎng)站 - BooksStore(一)》
《一步步打造一個(gè)簡(jiǎn)單的 MVC 電商網(wǎng)站 - BooksStore(二)》
《一步步打造一個(gè)簡(jiǎn)單的 MVC 電商網(wǎng)站 - BooksStore(三)》
《一步步打造一個(gè)簡(jiǎn)單的 MVC 電商網(wǎng)站 - BooksStore(四)》
簡(jiǎn)介
主要功能與知識(shí)點(diǎn)如下:
分類、產(chǎn)品瀏覽、購(gòu)物車、結(jié)算、CRUD(增刪改查) 管理、發(fā)郵件、分頁、模型綁定、認(rèn)證過濾器和單元測(cè)試等(預(yù)計(jì)四篇、周五、下周一和周二)。
【備注】項(xiàng)目使用 VS2015 + C#6 進(jìn)行開發(fā),有問題請(qǐng)發(fā)表在留言區(qū)哦,還有,頁面長(zhǎng)得比較丑,請(qǐng)見諒。
目錄
- 創(chuàng)建項(xiàng)目架構(gòu)
- 創(chuàng)建域模型實(shí)體
- 創(chuàng)建單元測(cè)試
- 創(chuàng)建控制器與視圖
- 創(chuàng)建分頁
- 加入樣式
一、創(chuàng)建項(xiàng)目架構(gòu)
1.新建一個(gè)解決方案“BooksStore”,并添加以下項(xiàng)目:

BooksStore.Domain:類庫,存放域模型和邏輯,使用 EF; BooksStore.WebUI:Web MVC 應(yīng)用程序,存放視圖和控制器,充當(dāng)顯示層,使用了 Ninject 作為 DI 容器; BoosStore.UnitTest:?jiǎn)卧獪y(cè)試,對(duì)上述兩個(gè)項(xiàng)目進(jìn)行測(cè)試?! ?/p>
Web MVC 為一個(gè)空的 MVC 項(xiàng)目:

2.添加項(xiàng)目引用(需要使用 NuGet):



這是不同項(xiàng)目需要引用的類庫和項(xiàng)目
3.設(shè)置 DI 容器
我們通過 Ninject ,創(chuàng)建一個(gè)自定義的工廠,一個(gè)名為NinjectControllerFactory 的類繼承DefaultControllerFactory(默認(rèn)的控制器工廠)。你也可以在里面添加自定義的代碼,改變 MVC 框架的默認(rèn)行為。

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 用新建的類來創(chuàng)建控制器對(duì)象。
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());
}
}
二、創(chuàng)建域模型實(shí)體

1.在圖中位置創(chuàng)建一個(gè)名為 Book 的實(shí)體類。
public class Book
{
/// <summary>
/// 標(biāo)識(shí)
/// </summary>
public int Id { get; set; }
/// <summary>
/// 名稱
/// </summary>
public string Name { get; set; }
/// <summary>
/// 描述
/// </summary>
public string Description { get; set; }
/// <summary>
/// 價(jià)格
/// </summary>
public decimal Price { get; set; }
/// <summary>
/// 分類
/// </summary>
public string Category { get; set; }
}
有了實(shí)體之后,我們應(yīng)該創(chuàng)建一個(gè)“庫”對(duì)該實(shí)體進(jìn)行操作,而這種持久化邏輯操作也應(yīng)該和域模型是進(jìn)行隔離的。
2.先定義一個(gè)接口 IbookRepository,在根目錄創(chuàng)建一個(gè)名為 Abstract 的文件夾,顧名思義就是應(yīng)該放置一些抽象的類,如接口。

public interface IBookRepository
{
IQueryable<Book> Books { get; }
}
我們通過該接口就可以得到對(duì)應(yīng)類的相關(guān)信息,而不需要去管該數(shù)據(jù)如何存儲(chǔ),以及存儲(chǔ)的位置,這就是存儲(chǔ)庫模式的本質(zhì)。
3.接下來,我們就需要對(duì)數(shù)據(jù)庫進(jìn)行操作了,我們使用簡(jiǎn)單的EF(ORM 對(duì)象關(guān)系模型) 去對(duì)數(shù)據(jù)庫進(jìn)行操作,所以需要自己通過 Nuget 下載 EF 的類庫。

4.因?yàn)橹岸x了接口類,接下來就應(yīng)該定義實(shí)現(xiàn)該接口的類了

安裝完之后,再次建立一個(gè)名為 Concrete 的文件夾,存放實(shí)例。
在里面創(chuàng)建一個(gè) EfDbContext 的類,派生于 DbContext,該類會(huì)為用戶要使用的數(shù)據(jù)庫中的每個(gè)表自動(dòng)的定義一個(gè)屬性。該屬性名為 Books,指定了表名,DbSet<Book> 表示為 Book 實(shí)體的表模型,Book 對(duì)象相當(dāng)于 Books 表中的行(記錄)。
public class EfDbContext : DbContext
{
public DbSet<Book> Books { get; set; }
}
再創(chuàng)建一個(gè)EfBookRepository 存儲(chǔ)庫類,它實(shí)現(xiàn) IBookRepository 接口,使用了上文創(chuàng)建的 EfDbContext 上下文對(duì)象,包含了具體的方法定義。
public class EfBookRepository : IBookRepository
{
private readonly EfDbContext _context = new EfDbContext();
public IQueryable<Book> Books => _context.Books;
}
5.現(xiàn)在只差在數(shù)據(jù)庫新建一張表了。
CREATE TABLE Book ( Id INT IDENTITY PRIMARY KEY, Name NVARCHAR(100), Description NVARCHAR(MAX), Price DECIMAL, Category NVARCHAR(50) )
并插入測(cè)試數(shù)據(jù):
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) )
測(cè)試數(shù)據(jù)

因?yàn)槲蚁M砻麨?Book,而不是 Books,所以我在之前的 Book 類上加上特性 [Table("Book")]:

三、創(chuàng)建單元測(cè)試
1.做完預(yù)熱操作后,你可能想立即以界面的的方式進(jìn)行顯示,別急,先用單元測(cè)試檢查一下我們對(duì)數(shù)據(jù)庫的操作是否正常,通過對(duì)數(shù)據(jù)進(jìn)行簡(jiǎn)單的讀取,檢查下連接是否成功。

2.單元測(cè)試也需要引入 ef 類庫(Nuget)。
3.安裝完之后會(huì)生成一個(gè) app.config 配置文件,需要額外添加一行連接字符串(在后續(xù)的 Web UI 項(xiàng)目里,也需要加上這條信息,不然會(huì)提示對(duì)應(yīng)的錯(cuò)誤信息)。
<connectionStrings> <add name="EfDbContext" connectionString="server=.;database=TestDb;uid=sa;pwd=123" providerName="System.Data.SqlClient"/> </connectionStrings>
4.當(dāng)所有前置工作都準(zhǔn)備好了的時(shí)候,就應(yīng)該填寫測(cè)試方法了,因?yàn)槲也迦肓?7 條數(shù)據(jù),這里我就判斷一下從數(shù)據(jù)庫讀取出的行數(shù)是否為 7 :
[TestMethod]
public void BooksCountTest()
{
var bookRepository=new EfBookRepository();
var books = bookRepository.Books;
Assert.AreEqual(books.Count(),7);
}
5.在該方法體的內(nèi)部單擊右鍵,你可以看到一個(gè)“運(yùn)行測(cè)試”的選項(xiàng),這時(shí)你可以嘗試單擊它:


從這個(gè)符號(hào)可以看到,是成功了!
接下來,我們要正式從頁面顯示我們想要的信息了。
四、創(chuàng)建控制器與視圖
1.先新建一個(gè)空的控制器:BookController:

2.需要我們自定義一個(gè) Details 方法,用于后續(xù)與界面進(jìn)行交互。
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.接下來,要?jiǎng)?chuàng)建一個(gè)視圖 View 了。

4.將 Details.cshtml 的內(nèi)容替換為下面的:
@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.改下默認(rèn)的路由機(jī)制,讓他默認(rèn)跳轉(zhuǎn)到該頁面。

6.還有一點(diǎn)需要注意的是,因?yàn)槲覀兪褂昧?Ninject 容器,并且需要對(duì)控制器中的構(gòu)造函數(shù)中的參數(shù)IBookRepository 進(jìn)行解析,告訴他將使用哪個(gè)對(duì)象對(duì)該接口進(jìn)行服務(wù),也就是需要修改之前的 AddBindings 方法:

7.運(yùn)行的效果大致如下(因?yàn)榧恿它c(diǎn) CSS 樣式,所以顯示的效果可能有些許不同),結(jié)果是一致的。

五、創(chuàng)建分頁
1.在 Models 文件夾新增一個(gè) PagingInfo.cs 分頁信息類。

/// <summary>
/// 分頁信息
/// </summary>
public class PagingInfo
{
/// <summary>
/// 總數(shù)
/// </summary>
public int TotalItems { get; set; }
/// <summary>
/// 頁容量
/// </summary>
public int PageSize { get; set; }
/// <summary>
/// 當(dāng)前頁
/// </summary>
public int PageIndex { get; set; }
/// <summary>
/// 總頁數(shù)
/// </summary>
public int TotalPages => (int)Math.Ceiling((decimal)TotalItems / PageSize);
}
2.新增一個(gè) HtmlHelpers 文件夾存放一個(gè)基于 Html 幫助類的擴(kuò)展方法:

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++)
{
//創(chuàng)建 <a> 標(biāo)簽
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.添加完畢后需要在配置文件內(nèi)加入該命名空間

4.現(xiàn)在要重新修改BookController.cs 控制器內(nèi)的的代碼,并添加新的視圖模型類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.修改視圖模型后,對(duì)應(yīng)的視圖頁也需要修改
@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.頁面的樣式簡(jiǎn)單的設(shè)計(jì)為 3 大板塊,頂部為標(biāo)題,左側(cè)邊欄為分類,主模塊將顯示具體內(nèi)容。
我們現(xiàn)在要在 Views 文件夾下創(chuàng)建一個(gè)文件 _ViewStart.cshtml,再創(chuàng)建一個(gè) Shared 的文件夾和文件 _Layout.cshtml。

2._Layout.cshtml 這是布局頁,當(dāng)代碼執(zhí)行到 @RenderBody() 時(shí),就會(huì)負(fù)責(zé)將之前 Details.cshtml 的內(nèi)容進(jìn)行渲染:
<!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 該文件表示默認(rèn)的布局頁為該視圖文件:
@{
Layout = "~/Views/Shared/_Layout.cshtml";
}
3.網(wǎng)站的根目錄下也要添加一個(gè)名為 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;
}

現(xiàn)在,分頁也已經(jīng)有了效果,基本界面就出來了。
本系列的 GitHub地址:https://github.com/liqingwen2015/Wen.BooksStore
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- MVC+EasyUI+三層新聞網(wǎng)站建立 建站準(zhǔn)備工作(一)
- MVC+EasyUI+三層新聞網(wǎng)站建立 主頁布局的方法(五)
- MVC+EasyUI+三層新聞網(wǎng)站建立 實(shí)現(xiàn)登錄功能(四)
- MVC+EasyUI+三層新聞網(wǎng)站建立 后臺(tái)登錄界面的搭建(二)
- MVC+EasyUI+三層新聞網(wǎng)站建立 驗(yàn)證碼生成(三)
- 一步步打造簡(jiǎn)單的MVC電商網(wǎng)站BooksStore(2)
- MVC4制作網(wǎng)站教程第四章 更新欄目4.3
- MVC4制作網(wǎng)站教程第四章 瀏覽欄目4.2
- MVC4制作網(wǎng)站教程第四章 添加欄目4.1
- MVC+EasyUI+三層新聞網(wǎng)站建立 tabs標(biāo)簽制作方法(六)
相關(guān)文章
asp.net用Zxing庫實(shí)現(xiàn)條形碼輸出的具體實(shí)現(xiàn)
這篇文章主要介紹了asp.net用Zxing庫實(shí)現(xiàn)條形碼輸出的具體實(shí)現(xiàn),有需要的朋友可以參考一下2013-12-12
關(guān)于前臺(tái)調(diào)用后臺(tái)事件__doPostBack函數(shù)
關(guān)于前臺(tái)調(diào)用后臺(tái)事件__doPostBack函數(shù)...2007-04-04
ASP.NET Core 3.x 并發(fā)限制的實(shí)現(xiàn)代碼
這篇文章主要介紹了ASP.NET Core 3.x 并發(fā)限制的實(shí)現(xiàn)代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11
asp.net顯示圖片到指定的Image控件中 具體實(shí)現(xiàn)
這篇文章介紹了asp.net顯示圖片到指定的Image控件中 具體實(shí)現(xiàn),有需要的朋友可以參考一下2013-11-11
asp.net實(shí)現(xiàn)的DES加密解密操作示例
這篇文章主要介紹了asp.net實(shí)現(xiàn)的DES加密解密操作,結(jié)合具體實(shí)例形式分析了asp.net實(shí)現(xiàn)DES加密與解密算法的實(shí)現(xiàn)技巧,需要的朋友可以參考下2017-07-07
viewstate和datatable動(dòng)態(tài)錄入數(shù)據(jù)示例
這篇文章主要介紹了viewstate和datatable動(dòng)態(tài)錄入數(shù)據(jù)示例,需要的朋友可以參考下2014-02-02
vs.net 2010 擴(kuò)展插件小結(jié) 提高編程效率
本文價(jià)紹了幾款Visual Studio提供的插件,提高我們的編程效率。2011-03-03

