SportsStore是《精通ASP.NET MVC3框架(第三版)》中演示的MVC專案,在該專案中涵蓋了MVC的眾多方面,包括:使用DI容器、URL優化、導航、分頁、購物車、訂單、產品管理、影象上傳......是不錯的MVC實踐專案,但該專案不是放在多層框架下開發的,離真實專案還有一段距離。本系列將嘗試在多層框架下實現SportsStore專案,並用自己的方式實現一些功能。
本篇為系列第六篇,包括:
■ 8、購物車
□ 8.1 購物車模型 購物車幫助類
□ 8.2 新增"放入購物車"按鈕
□ 8.3 顯示購物車內容 新增"移除"按鈕
□ 8.4 顯示購物車摘要
8、購物車
8.1 購物車模型 購物車幫助類
從邏輯上講,購物車幫助類不僅僅是針對Product的處理,還需考慮每種Product的數量。我們把這2個因素綜合起來封裝成一個基本單元:
public class CartLine { public Product Product { get; set; } public int Quantity { get; set; } }
而購物車幫助類的主要工作就是針對這個CartLine集合的各種處理:新增、移除、計算總價、清空等。
using System.Collections.Generic; using System.Linq; using MySportsStore.Model; namespace MySportsStore.WebUI.Models { public class Cart { private List<CartLine> lineCollection = new List<CartLine>(); //新增 public void AddItem(Product product, int quantity) { CartLine line = lineCollection.Where(p => p.Product.Id == product.Id).FirstOrDefault(); if (line == null) { lineCollection.Add(new CartLine(){Product = product, Quantity = quantity}); } else { line.Quantity += quantity; } } //移除 public void RemoveLine(Product product) { lineCollection.RemoveAll(p => p.Product.Id == product.Id); } //計算總價 public decimal ComputeTotalValue() { return lineCollection.Sum(p => p.Product.Price*p.Quantity); } //清空 public void Clear() { lineCollection.Clear(); } //獲取 public IEnumerable<CartLine> Lines { get { return lineCollection; } } } }
8.2 新增"放入購物車"按鈕
在顯示產品的部分檢視中,新增"放入購物車"按鈕,把Produect的Id和當前頁的URL作為隱藏域傳遞給控制器方法:
@model MySportsStore.Model.Product <div class="item"> <h3>@Model.Name</h3> @Model.Description @using (Html.BeginForm("AddToCart", "Cart")) { @Html.HiddenFor(p => p.Id) @Html.Hidden("returnUrl", Request.Url.PathAndQuery) <input type="submit" value="+放入購物車"/> } <h4>@Model.Price.ToString("c")</h4> </div>
在即將建立的Cart控制器方法中,肯定要用到Cart類的例項,而該例項將貫穿於頁面之間,所以應該存放於Session中。首先想到的是通過如下方式獲取Cart類的例項:
private Cart GetCart() { Cart cart = (Cart)Session["Cart"]; if (cart == null) { cart = new Cart(); Session["Cart"] = cart; } return cart; }
也可以自定義一個ModelBinder,使之從Session中獲取Cart類的例項:
using System.Web.Mvc; using MySportsStore.WebUI.Models; namespace MySportsStore.WebUI.Extension { public class CartModelBinder : IModelBinder { private const string sessionKey = "Cart"; public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { Cart cart = (Cart)controllerContext.HttpContext.Session[sessionKey]; if (cart == null) { cart = new Cart(); controllerContext.HttpContext.Session[sessionKey] = cart; } return cart; } } }
在全域性檔案Global.asax中把自定義的ModelBinder註冊到MVC中:
protected void Application_Start() { ...... ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory()); ModelBinders.Binders.Add(typeof(Cart), new CartModelBinder()); }
有了如上鋪墊,在Cart控制器的方法中引數中,可以有一個型別為Cart的引數,該引數值直接從自定義的CartModelBinder中獲取:
using System.Linq; using System.Web.Mvc; using MySportsStore.IBLL; using MySportsStore.Model; using MySportsStore.WebUI.Models; using Ninject; namespace MySportsStore.WebUI.Controllers { public class CartController : BaseController { [Inject] public IProductService ProductService { get; set; } public CartController() { this.AddDisposableObject(ProductService); } public ActionResult Index(Cart cart, string returnUrl) { return View(new CartIndexViewModel { //Cart = GetCart(), Cart = cart, ReturnUrl = returnUrl }); } //新增到購物車 public RedirectToRouteResult AddToCart(Cart cart, int Id, string returnUrl) { Product product = ProductService.LoadEntities(p => p.Id == Id).FirstOrDefault(); if (product != null) { //GetCart().AddItem(product, 1); cart.AddItem(product, 1); } return RedirectToAction("Index", new {returnUrl}); } //從購物車移除 public RedirectToRouteResult RemoveFromCart(Cart cart, int Id, string returnUrl) { Product product = ProductService.LoadEntities(p => p.Id == Id).FirstOrDefault(); if (product != null) { //GetCart().RemoveLine(product); cart.RemoveLine(product); } return RedirectToAction("Index", new {returnUrl}); } public ViewResult Summary(Cart cart) { return View(cart); } private Cart GetCart() { Cart cart = (Cart)Session["Cart"]; if (cart == null) { cart = new Cart(); Session["Cart"] = cart; } return cart; } } }
執行:
顯示購物車內容 新增"移除"按鈕
在顯示購物車內容檢視頁,除了要把購物車內容呈現,在其對應的檢視模型中還必須有一個屬性,用來存放先前的URL,然後點選頁面的"繼續購物"按鈕,方才可以回到先前的介面。
顯示購物車內容檢視頁的View Model為:
namespace MySportsStore.WebUI.Models { public class CartIndexViewModel { public Cart Cart { get; set; } public string ReturnUrl { get; set; } } }
Cart/Index.cshtml檢視:
@model MySportsStore.WebUI.Models.CartIndexViewModel @{ ViewBag.Title = "Index"; Layout = "~/Views/Shared/_Layout.cshtml"; } <table width="50%" align="left"> <thead> <tr> <th align="left">產品名稱</th> <th align="center">數量</th> <th align="right">單價</th> <th align="right">小計</th> <th></th> </tr> </thead> <tbody> @foreach (var line in Model.Cart.Lines) { <tr> <td align="left">@line.Product.Name</td> <td align="center">@line.Quantity</td> <td align="right">@line.Product.Price.ToString("c")</td> <td align="right">@((line.Quantity * line.Product.Price).ToString("c"))</td> <td> @using (Html.BeginForm("RemoveFromCart", "Cart")) { @Html.Hidden("Id", line.Product.Id) @Html.HiddenFor(x => x.ReturnUrl) <input class="actionButtons" type="submit" value="移除"/> } </td> </tr> } </tbody> <tfoot> <tr> <td colspan="3" align="right">總計:</td> <td align="right">@Model.Cart.ComputeTotalValue().ToString("c")</td> </tr> </tfoot> </table> <p align="left" class="actionButtons" style="width: 100%;clear: both"> <a href="@Model.ReturnUrl">繼續購物</a> </p>
執行:
8.4 顯示購物車摘要
購物車摘要通常放在頁面的公共部分,用來顯示買了多少件商品,花了多少錢,並提供一個指向購物車顯示頁的連結,以部分檢視的形式存在:
@model MySportsStore.WebUI.Models.Cart @{ Layout = null; } <div id="cart"> <span class="caption"> <b>購物車明細:</b> @Model.Lines.Sum(x => x.Quantity) 件, @Model.ComputeTotalValue().ToString("c") </span> @Html.ActionLink("結算", "Index", "Cart", new {returnUrl = Request.Url.PathAndQuery}, null) </div>
然後把這塊部分檢視放到頁面的公共部分,即Views/Shared/_Layout.cshtml中:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width" /> <title>@ViewBag.Title</title> <link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" /> </head> <body> <div id="header" style="height: 200px;"> @{Html.RenderAction("Summary", "Cart");} <div class="title">體育用品商店</div> </div> <div id="categories"> @{Html.RenderAction("Menu","Nav");} </div> <div id="content"> @RenderBody() </div> @Scripts.Render("~/bundles/jquery") @RenderSection("scripts", required: false) </body> </html>
執行:
至此,購物車功能結束。
原始碼在這裡。
“MVC專案實踐,在三層架構下實現SportsStore”系列包括: