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”系列包括:

MVC项目实践,在三层架构下实现SportsStore,从类图看三层架构

MVC项目实践,在三层架构下实现SportsStore-01,EF Code First建模、DAL层等

MVC项目实践,在三层架构下实现SportsStore-02,DbSession层、BLL层

MVC项目实践,在三层架构下实现SportsStore-03,Ninject控制器工厂等

MVC项目实践,在三层架构下实现SportsStore-04,实现分页

MVC项目实践,在三层架构下实现SportsStore-05,实现导航

MVC项目实践,在三层架构下实现SportsStore-06,实现购物车

MVC项目实践,在三层架构下实现SportsStore-07,实现订单提交

MVC项目实践,在三层架构下实现SportsStore-08,部署到IIS服务器

MVC项目实践,在三层架构下实现SportsStore-09,ASP.NET MVC调用ASP.NET Web API的查询服务

MVC项目实践,在三层架构下实现SportsStore-10,连接字符串的加密和解密

MVC项目实践,在三层架构下实现SportsStore-11,使用Knockout实现增删改查

MVC项目实践,在三层架构下实现SportsStore-06,实现购物车的更多相关文章

  1. MVC项目实践,在三层架构下实现SportsStore,从类图看三层架构

    在"MVC项目实践,在三层架构下实现SportsStore-02,DbSession层.BLL层"一文的评论中,博友浪花一朵朵建议用类图来理解本项目的三层架构.于是就有了本篇: I ...

  2. MVC项目实践,在三层架构下实现SportsStore-02,DbSession层、BLL层

    SportsStore是<精通ASP.NET MVC3框架(第三版)>中演示的MVC项目,在该项目中涵盖了MVC的众多方面,包括:使用DI容器.URL优化.导航.分页.购物车.订单.产品管 ...

  3. MVC项目实践,在三层架构下实现SportsStore-01,EF Code First建模、DAL层等

    SportsStore是<精通ASP.NET MVC3框架(第三版)>中演示的MVC项目,在该项目中涵盖了MVC的众多方面,包括:使用DI容器.URL优化.导航.分页.购物车.订单.产品管 ...

  4. MVC项目实践,在三层架构下实现SportsStore-03,Ninject控制器工厂等

    SportsStore是<精通ASP.NET MVC3框架(第三版)>中演示的MVC项目,在该项目中涵盖了MVC的众多方面,包括:使用DI容器.URL优化.导航.分页.购物车.订单.产品管 ...

  5. MVC项目实践,在三层架构下实现SportsStore-04,实现分页

    SportsStore是<精通ASP.NET MVC3框架(第三版)>中演示的MVC项目,在该项目中涵盖了MVC的众多方面,包括:使用DI容器.URL优化.导航.分页.购物车.订单.产品管 ...

  6. MVC项目实践,在三层架构下实现SportsStore-05,实现导航

    SportsStore是<精通ASP.NET MVC3框架(第三版)>中演示的MVC项目,在该项目中涵盖了MVC的众多方面,包括:使用DI容器.URL优化.导航.分页.购物车.订单.产品管 ...

  7. MVC项目实践,在三层架构下实现SportsStore-07,实现订单提交

    SportsStore是<精通ASP.NET MVC3框架(第三版)>中演示的MVC项目,在该项目中涵盖了MVC的众多方面,包括:使用DI容器.URL优化.导航.分页.购物车.订单.产品管 ...

  8. MVC项目实践,在三层架构下实现SportsStore-08,部署到IIS服务器

    SportsStore是<精通ASP.NET MVC3框架(第三版)>中演示的MVC项目,在该项目中涵盖了MVC的众多方面,包括:使用DI容器.URL优化.导航.分页.购物车.订单.产品管 ...

  9. MVC项目实践,在三层架构下实现SportsStore-09,ASP.NET MVC调用ASP.NET Web API的查询服务

    ASP.NET Web API和WCF都体现了REST软件架构风格.在REST中,把一切数据视为资源,所以也是一种面向资源的架构风格.所有的资源都可以通过URI来唯一标识,通过对资源的HTTP操作(G ...

随机推荐

  1. POJ 3254 Corn Fields(状压DP)

    Corn Fields Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 13732   Accepted: 7216 Desc ...

  2. 分布式文件系统FastDFS原理介绍

    在生产中我们一般希望文件系统能帮我们解决以下问题,如:1.超大数据存储:2.数据高可用(冗余备份):3.读/写高性能:4.海量数据计算.最好还得支持多平台多语言,支持高并发. 由于单台服务器无法满足以 ...

  3. [收藏] 关于解决“进程com.android.phone意外停止”的方法 (未尝试)

    很多机油反应有这个情况,本人费劲九牛20虎之力终于克服之,这个现象一般出现在刚刷完系统会出现,甚至你怎么刷ROM这个现象依旧存在(崩溃不?)~~~有位机油刷了这个系统也出现了http://samsun ...

  4. mysql 锁

    Lock table有两种模式 lock tables table_name  read  [or write]; test1: session 1: lock tables tmp_xf_lock; ...

  5. javaWeb中servlet开发——监听器

    监听的定义 对application的监听 application是servletContext接口的对象,表示的是整个上下文环境,如果要想实现对application监听则可以使用如下两个接口: s ...

  6. Decoder with 3 Inputs and 2 3 = 8 Outputs

    COMPUTER ORGANIZATION AND ARCHITECTURE DESIGNING FOR PERFORMANCE NINTH EDITION

  7. wajueji

    #include<stdio.h>int map[3]={42,3,99};int step[3]={0};int max=99999;void qian(){ int i=0; int ...

  8. Yii2 捕获错误日志

    在技术开发中,捕获程序框架错误,是非常必要的一件事情,我们公司使用Yii2框架,简单说下Yii2的错误捕获处理 Yii2 web应用 1 配置如下 其中errorHandler就是错误处理配置,执行E ...

  9. 如何在外网中访问自己在另一个局域网中的某个机器(SSH为例)

    UBUNTU 14.04 LTS 为例 如何在外网中访问自己在另一个局域网中的某个机器(SSH为例) 2013-05-01 16:02 2693人阅读 评论(0) 收藏 举报 情景描述: 计算机C1放 ...

  10. 转:ASP.NET MVC利用TryUpdateModel来做资料更新 (二)

    前言 第一篇說明了 TryUpdateModel 的簡單的應用,除了可指定更新的欄位之外也可排除更新特定的欄位,而因為可搭配 Metadata 做欄位驗證為資料又做了一層把關,但在 ASP.NET M ...