Build a simple MVC e-commerce website booksstore step by step (1)

Time:2021-11-24

Build a simple MVC e-commerce website – booksstore step by step (I)

GitHub address of this series:https://github.com/liqingwen2015/Wen.BooksStore

Build a simple MVC e-commerce website – booksstore step by step (I)

Build a simple MVC e-commerce website – booksstore step by step (II)

Build a simple MVC e-commerce website – booksstore step by step (III)

Build a simple MVC e-commerce website – booksstore step by step (IV)

brief introduction

The main functions and knowledge points are as follows:

Classification, product browsing, shopping cart, settlement, CRUD (addition, deletion, modification and query) management, e-mail, paging, model binding, authentication filter and unit test (expected four articles, Friday, next Monday and Tuesday).

[remarks] the project uses vs2015 + c#6 for development. If you have any questions, please post them in the message area. In addition, the page is ugly. Please forgive me.

catalogue

  • Create project schema
  • Create domain model entity
  • Create unit test
  • Creating controllers and views
  • Create paging
  • Add style

1、 Create project schema

1. Create a new solution “booksstore” and add the following items:

Booksstore. Domain: class library, storing domain model and logic, using EF; Booksstore. Webui: Web MVC application, which stores views and controllers, acts as the display layer, and uses ninject as the di container; Boosstore. Unittest: unit test, which tests the above two items.

Web MVC is an empty MVC project:

2. Add project reference (nuget is required):

This is the class library and project that need to be referenced by different projects

3. Set Di container

Through ninject, we create a custom factory. A class named ninjectcontrollerfactory inherits defaultcontrollerfactory (the default controller factory). You can also add custom code to change the default behavior of the MVC framework.

Addbindings() add binding method, leave blank first.

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>
 ///Add binding
 /// </summary>
 private void AddBindings()
 {
  
 }
 }

4. Add a line of code in global.asax to tell MVC to create the controller object with the new class.

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());
 }
 }

2、 Create domain model entity

1. Create an entity class named book at the position in the figure.

public class Book
 {
 /// <summary>
 ///Identification
 /// </summary>
 public int Id { get; set; }

 /// <summary>
 ///Name
 /// </summary>
 public string Name { get; set; }

 /// <summary>
 ///Description
 /// </summary>
 public string Description { get; set; }

 /// <summary>
 ///Price
 /// </summary>
 public decimal Price { get; set; }

 /// <summary>
 ///Classification
 /// </summary>
 public string Category { get; set; }
 }

After having an entity, we should create a “library” to operate on the entity, and this persistence logic operation should also be isolated from the domain model.

2. First define an interface ibookrepository and create a folder named abstract in the root directory. As the name implies, some abstract classes, such as interfaces, should be placed.


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

Through this interface, we can get the relevant information of the corresponding class, regardless of how and where the data is stored. This is the essence of the repository pattern.

3. Next, we need to operate the database. We use the simple ef (ORM object relational model) to operate the database, so we need to download the EF class library through nuget.

4. Because the interface class was defined before, the class implementing the interface should be defined next

After installation, create a folder named concrete again to store instances.

Create a class of efdbcontext derived from dbcontext. This class will automatically define an attribute for each table in the database to be used by the user. This attribute is named books, specifies the table name, dbset < book > represents the table model of the book entity, and the book object is equivalent to the row (record) in the books table.


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

Then create an efbookrepository repository class, which implements the ibookrepository interface, uses the efdbcontext context context object created above, and contains specific method definitions.


public class EfBookRepository : IBookRepository
 {
 private readonly EfDbContext _context = new EfDbContext();

 public IQueryable<Book> Books => _context.Books;
 }

5. Now we only need to create a new table in the database.


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

And insert test data:

INSERT INTO dbo.Book
 ( 
  Name ,
  Description ,
  Price ,
  Category
 )
VALUES ( 
  N'c# from introduction to mastery ', -- name - nvarchar (100)
  N 'good book -c# from introduction to mastery', -- Description - nvarchar (max)
, -- Price - decimal
  N'.NET' -- Category - nvarchar(50)
 )

INSERT INTO dbo.Book
 ( 
  Name ,
  Description ,
  Price ,
  Category
 )
VALUES ( 
  N'asp.net from entry to mastery ', -- name - nvarchar (100)
  N 'good book - asp.net from introduction to mastery', -- Description - nvarchar (max)
, -- Price - decimal
  N'.NET' -- Category - nvarchar(50)
 )

INSERT INTO dbo.Book
 ( 
  Name ,
  Description ,
  Price ,
  Category
 )
VALUES ( 
  N 'multithreading from entry to mastery', -- name - nvarchar (100)
  N 'good book - multithreading from entry to mastery', -- Description - nvarchar (max)
, -- Price - decimal
  N'.NET' -- Category - nvarchar(50)
 )

INSERT INTO dbo.Book
 ( 
  Name ,
  Description ,
  Price ,
  Category
 )
VALUES ( 
  N'java from getting started to giving up ', -- name - nvarchar (100)
  N 'good book - Java from getting started to giving up', -- Description - nvarchar (max)
, -- Price - decimal
  N'java' -- Category - nvarchar(50)
 )

INSERT INTO dbo.Book
 ( 
  Name ,
  Description ,
  Price ,
  Category
 )
VALUES ( 
  N'sql from getting started to giving up ', -- name - nvarchar (100)
  N 'good book - SQL from getting started to giving up', -- Description - nvarchar (max)
, -- Price - decimal
  N'sql' -- Category - nvarchar(50)
 )

INSERT INTO dbo.Book
 ( 
  Name ,
  Description ,
  Price ,
  Category
 )
VALUES ( 
  N'sql from getting started to becoming a monk ', -- name - nvarchar (100)
  N 'good book - SQL from getting started to becoming a monk', -- Description - nvarchar (max)
, -- Price - decimal
  N'sql' -- Category - nvarchar(50)
 )

INSERT INTO dbo.Book
 ( 
  Name ,
  Description ,
  Price ,
  Category
 )
VALUES ( 
  N'php from getting started to becoming a monk ', -- name - nvarchar (100)
  N 'good book - PHP from getting started to becoming a monk', -- Description - nvarchar (max)
, -- Price - decimal
  N'php' -- Category - nvarchar(50)
 )

test data

Because I want the table name to be book instead of books, I add the feature [table (“book”)] to the previous book class:

3、 Create unit test

1. After the preheating operation, you may want to display it in the form of interface immediately. Don’t worry. First check whether our operation on the database is normal with unit test. Check whether the connection is successful by simply reading the data.

2. The EF class library (nuget) should also be introduced for unit testing.

3. After installation, an app.config configuration file will be generated, and an additional line of connection string needs to be added (this information also needs to be added in subsequent web UI projects, otherwise the corresponding error message will be prompted).


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

4. When all the front-end work is ready, the test method should be filled in, because I have inserted 7 pieces of data. Here I will judge whether the number of rows read from the database is 7:


[TestMethod]
 public void BooksCountTest()
 {
  var bookRepository=new EfBookRepository();
  var books = bookRepository.Books;

  Assert.AreEqual(books.Count(),7);
 }

5. Right click inside the method body to see an option of “run test”. At this time, you can try to click it:

As can be seen from this symbol, it is successful!

Next, we will officially display the information we want from the page.

4、 Creating controllers and views

1. First create an empty controller: bookcontroller:

2. We need to customize a details method for subsequent interaction with the interface.

public class BookController : Controller
 {
 private readonly IBookRepository _bookRepository;

 public BookController(IBookRepository bookRepository)
 {
  _bookRepository = bookRepository;
 }

 /// <summary>
 ///Details
 /// </summary>
 /// <returns></returns>
 public ActionResult Details()
 {
  return View(_bookRepository.Books);
 }
 }

3. Next, create a view.

4. Replace details.cshtml with the following:


@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. Change the default routing mechanism and let him jump to this page by default.

6. Another thing to note is that we use the ninject container and need to resolve the parameter ibookrepository in the constructor in the controller to tell him which object will be used to service the interface, that is, we need to modify the previous addbindings method:

7. The running effect is roughly as follows (the displayed effect may be slightly different because a little CSS style is added), and the results are consistent.

5、 Create paging

1. Add a paginginfo.cs paging information class in the models folder.

/// <summary>
 ///Paging information
 /// </summary>
 public class PagingInfo
 {
 /// <summary>
 ///Total
 /// </summary>
 public int TotalItems { get; set; }

 /// <summary>
 ///Page capacity
 /// </summary>
 public int PageSize { get; set; }

 /// <summary>
 ///Current page
 /// </summary>
 public int PageIndex { get; set; }

 /// <summary>
 ///Total pages
 /// </summary>
 public int TotalPages => (int)Math.Ceiling((decimal)TotalItems / PageSize);
 }

2. Add an HTML helpers folder to store an extension method based on HTML help class:

public static class PagingHelper
 {
 /// <summary>
 ///Pagination
 /// </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++)
  {
  //Create < a > tag
  var tagBuilder = new TagBuilder("a");
  //Add properties
  tagBuilder.MergeAttribute("href", func(i));
  //Add value
  tagBuilder.InnerHtml = i.ToString();

  if (i == pagingInfo.PageIndex)
  {
   tagBuilder.AddCssClass("selected");
  }

  sb.Append(tagBuilder);
  }

  return MvcHtmlString.Create(sb.ToString());
 }
 }

3. Add the namespace in the configuration file after adding

4. Now modify the code in the bookcontroller.cs controller again and add a new view model class bookdetailsviewmodels.cs to inherit the previous paging class.


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

Modified controller code:

public class BookController : Controller
 {
 private readonly IBookRepository _bookRepository;
 public int PageSize = 5;

 public BookController(IBookRepository bookRepository)
 {
  _bookRepository = bookRepository;
 }

 /// <summary>
 ///Details
 /// </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. After modifying the view model, the corresponding view page also needs to be modified


@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>
 @Html.PageLinks(Model, x => Url.Action("Details", new { pageIndex = x }))
</div>

6、 Add style

1. The style of the page is simple. It is designed into three sections, the top is the title, the left sidebar is the classification, and the main module will display the specific content.

We will now create a file under the views folder_ Viewstart.cshtml, and then create a shared folder and file_ Layout.cshtml。

2._ Layout.cshtml is the layout page. When the code is executed to @ renderbody(), it will be responsible for rendering the contents of previous 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>
 < div > Book Mall < / div >
 </div>
 < div > classification < / div >
 <div>
 @RenderBody()
 </div>
</body>
</html>

_ Viewstart.cshtml this file indicates that the default layout page is the view file:


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

3. A folder named contents should also be added under the root directory of the website to store 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;
 }

Now, paging has also had an effect, and the basic interface comes out.

GitHub address of this series:https://github.com/liqingwen2015/Wen.BooksStore

The above is the whole content of this article. I hope it will be helpful to your study, and I hope you can support developpaer.