购物车示例

添加Cart、Order和OrderDetails模型类

购物和结算都要使用某些新的类,在Models文件夹上单击右键添加Cart类(Card.cs),代码如下:

using System.ComponentModel.DataAnnotations;

namespace MvcMusicStore.Models

{

public class Cart

{

[Key]

public int      RecordId    { get; set; }

public string   CartId      { get; set; }

public int      AlbumId     { get; set; }

public int      Count       { get; set; }

public System.DateTime DateCreated { get; set; }

public virtual Album Album  { get; set; }

}

}

除了RecordId属性的[KEY]特性外,这个类与目前我们使用的其它类非常相似。Cart有一个叫CartId的字符串标识,以便允许匿名者购物,但是RecordId是表的整形主键。习惯上,实体框架“代码优先”会认为Cart表的主键是CartId或ID,而如果需要的话,我们很容易通过注释或代码忽略这一点。上面就是一个例子,当实体框架“代码优先”的约定适合我们时,我们就使用,反之,我们也不会被这些约定限制。

接下来添中Order类(Order.cs):

using System.Collections.Generic;

namespace MvcMusicStore.Models

{

public partial class Order

{

public int    OrderId    { get; set; }

public string Username   { get; set; }

public string FirstName  { get; set; }

public string LastName   { get; set; }

public string Address    { get; set; }

public string City       { get; set; }

public string State      { get; set; }

public string PostalCode { get; set; }

public string Country    { get; set; }

public string Phone      { get; set; }

public string Email      { get; set; }

public decimal Total     { get; set; }

public System.DateTime OrderDate      { get; set; }

public List<OrderDetail> OrderDetails { get; set; }

}

}

这个类记录订单摘要和交货信息,它还不能编译,因为里面有一个OrderDetails导航属性依赖的类我们还没创建,让我们添加一个OrderDetails类(OrderDetails.cs):

namespace MvcMusicStore.Models

{

public class OrderDetail

{

public int OrderDetailId { get; set; }

public int OrderId { get; set; }

public int AlbumId { get; set; }

public int Quantity { get; set; }

public decimal UnitPrice { get; set; }

public virtual Album Album { get; set; }

public virtual Order Order { get; set; }

}

}

现在做最后一个更新,在MusicStoreEntities类中添加这些新模型类的DbSet,更新后的MusicStoreEntities类如下:

using System.Data.Entity;

namespace MvcMusicStore.Models

{

public class MusicStoreEntities : DbContext

{

public DbSet<Album>     Albums  { get; set; }

public DbSet<Genre>     Genres  { get; set; }

public DbSet<Artist>    Artists { get; set; }

public DbSet<Cart>      Carts { get; set; }

public DbSet<Order>     Orders { get; set; }

public DbSet<OrderDetail> OrderDetails { get; set; }

}

}

管理购物车的业务逻辑

下面我们在Models文件夹中创建一个ShoppingCart类,ShoppingCart模型处理对Cart表的数据访问,并且还处理往购物车添加和移除商品的业务逻辑。

我们不想让用户仅仅为了往购物车内放一个商品就去申请帐号,当他们访问购物车时,我们给他们分配一个临时的唯一标识(使用GUID,全球唯一标识符),使用ASP.NET的Session来保存这个ID。

注: ASP.NET Session 能够方便地保存用户特定信息,并且在用户离开网站后自动过期。虽然滥用Sission会对较大站点产生影响,为了演示目的的轻量级使用没有任何问题。

ShoppingCart类公开以下方法:

AddToCart:以一个Album对象为参数,并把它添加到用户的购物车中。因为Cart表记录每种唱片的数量,方法中包括在需要时创建一个新行或者当用户拿取已有的唱片时增加该唱片的数量的业务逻辑。

RemoveFromCart:以Album ID为参数,并从用户的购物车中把它移除。如果用户的购物车中该唱片只有一份,则删除一行。

EmptyCart:从用户的购物车中移除所有商品

GetCartItems:返回购物车中的商品列表

GetCount:返回用户购物车中的唱片的总数

GetTotal:计算用户购物车中商品的总金额

CreateOrder:在结算阶段从购物车生成一张订单

GetCart:这是一个静态方法,允许控制器获得一个Cart对象。它调用GetCartId方法从用户的Session中读取CartId。GetCartId需要HttpContextBase以从用户的Sission中读取CartId。

下面是完整的ShoppingCart类的代码:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Web;

using System.Web.Mvc;

namespace MvcMusicStore.Models

{

public partial class ShoppingCart

{

MusicStoreEntities storeDB = new MusicStoreEntities();

string ShoppingCartId { get; set; }

public const string CartSessionKey = "CartId";

public static ShoppingCart GetCart(HttpContextBase context)

{

var cart = new ShoppingCart();

cart.ShoppingCartId = cart.GetCartId(context);

return cart;

}

// Helper method to simplify shopping cart calls

public static ShoppingCart GetCart(Controller controller)

{

return GetCart(controller.HttpContext);

}

public void AddToCart(Album album)

{

// Get the matching cart and album instances

var cartItem = storeDB.Carts.SingleOrDefault(

c => c.CartId == ShoppingCartId

&& c.AlbumId == album.AlbumId);

if (cartItem == null)

{

// Create a new cart item if no cart item exists

cartItem = new Cart

{

AlbumId = album.AlbumId,

CartId = ShoppingCartId,

Count = 1,

DateCreated = DateTime.Now

};

storeDB.Carts.Add(cartItem);

}

else

{

// If the item does exist in the cart, then add one to the

quantity

cartItem.Count++;

}

// Save changes

storeDB.SaveChanges();

}

public int RemoveFromCart(int id)

{

// Get the cart

var cartItem = storeDB.Carts.Single(

cart => cart.CartId == ShoppingCartId

&& cart.RecordId == id);

int itemCount = 0;

if (cartItem != null)

{

if (cartItem.Count > 1)

{

cartItem.Count--;

itemCount = cartItem.Count;

}

else

{

storeDB.Carts.Remove(cartItem);

}

// Save changes

storeDB.SaveChanges();

}

return itemCount;

}

public void EmptyCart()

{

var cartItems = storeDB.Carts.Where(cart => cart.CartId ==

ShoppingCartId);

foreach (var cartItem in cartItems)

{

storeDB.Carts.Remove(cartItem);

}

// Save changes

storeDB.SaveChanges();

}

public List<Cart> GetCartItems()

{

return storeDB.Carts.Where(cart => cart.CartId ==

ShoppingCartId).ToList();

}

public int GetCount()

{

// Get the count of each item in the cart and sum them up

int? count = (from cartItems in storeDB.Carts

where cartItems.CartId == ShoppingCartId

select (int?)cartItems.Count).Sum();

// Return 0 if all entries are null

return count ?? 0;

}

public decimal GetTotal()

{

// Multiply album price by count of that album to get

// the current price for each of those albums in the cart

// sum all album price totals to get the cart total

decimal? total = (from cartItems in storeDB.Carts

where cartItems.CartId == ShoppingCartId

select (int?)cartItems.Count *

cartItems.Album.Price).Sum();

return total ?? decimal.Zero;

}

public int CreateOrder(Order order)

{

decimal orderTotal = 0;

var cartItems = GetCartItems();

// Iterate over the items in the cart, adding the order details for

each

foreach (var item in cartItems)

{

var orderDetails = new OrderDetail

{

AlbumId = item.AlbumId,

OrderId = order.OrderId,

UnitPrice = item.Album.Price,

Quantity = item.Count

};

// Set the order total of the shopping cart

orderTotal += (item.Count * item.Album.Price);

}

// Set the order‘s total to the orderTotal count

order.Total = orderTotal;

// Save the order

storeDB.SaveChanges();

// Empty the shopping cart

EmptyCart();

// Return the OrderId as the confirmation number

return order.OrderId;

}

// We‘re using HttpContextBase to allow access to cookies.

public string GetCartId(HttpContextBase context)

{

if (context.Session[CartSessionKey] == null)

{

if (!string.IsNullOrWhiteSpace(context.User.Identity.Name))

{

context.Session[CartSessionKey] =

context.User.Identity.Name;

}

else

{

// Generate a new random GUID using System.Guid class

Guid tempCartId = Guid.NewGuid();

// Send tempCartId back to client as a cookie

context.Session[CartSessionKey] = tempCartId.ToString();

}

}

return context.Session[CartSessionKey].ToString();

}

// When a user has logged in, migrate their shopping cart to

// be associated with their username

public void MigrateCart(string userName)

{

var shoppingCart = storeDB.Carts.Where(c => c.CartId == ShoppingCartId);

foreach (Cart item in shoppingCart)

{

item.CartId = userName;

}

storeDB.SaveChanges();

}

}

模型视图

我们的ShoppingCart需要传递一些复合信息给它的视图,这些信息并没有清晰地映射到模型类。我们不想为了视图而修改模型,模型类应当代表我们的领域,而不是用户界面。一种解决方法是使用ViewBag传递相关信息,就象之前在StoreManager中为下拉框传递数据一样。但是通过ViewBag传递太多信息会变得难以管理。

另一种方案是使用视图模型(ViewModels),这种方式可以为指定的视图创建最优的强类型类,并且在视图模板中为所需的值/内容公开属性。控制器可以填充和传递这些“视图优化”类给视图模板使用,而且在视图模板中支持类型安全、编译检查和智能感应器。

我们创建两个视图模型供ShoppingCartController使用:ShoppingCartViewModel保存用户购物车的内容,ShoppingCartRemoveViewModel在用户从购物车移除物品时显示确认信息。

在项目中添加新文件夹ViewModels。

接下来在ViewModels文件夹中添加类ShoppingCartViewModel,它有两个属性:购物车物品集合和购物车所有物品的总金额。

using System.Collections.Generic;

using MvcMusicStore.Models;

namespace MvcMusicStore.ViewModels

{

public class ShoppingCartViewModel

{

public List<Cart> CartItems { get; set; }

public decimal CartTotal { get; set; }

}

}

在ViewModels文件夹下添加ShoppingCartRemoveViewModel类:

namespace MvcMusicStore.ViewModels

{

public class ShoppingCartRemoveViewModel

{

public string Message { get; set; }

public decimal CartTotal { get; set; }

public int CartCount { get; set; }

public int ItemCount { get; set; }

public int DeleteId { get; set; }

}

}

Shopping Cart控制器

Shopping Cart控制器有三个主要功能:往购物车添加物品,从购物车中移除物品以及显示购物车的物品。它将使用我们刚刚创建的三个类:ShoppingCartViewModel, ShoppingCartRemoveViewModel和 ShoppingCart。和StoreController发及StoreManagerController一样,我们为它添加一个MusicStoreEntities类的实例字段。

在项目中添加一个Shopping Cart控制器:

ASP.NET MVC Music Store教程(8):购物车和AJAX更新 - firechun - firechun的博客

下面是完整的ShoppingCartController的代码。Index和Add控制器操作看起来非常眼熟。Remove和CartSummary控制器操作处理两个特殊的事件,我们在下一章讨论它们。

using System.Linq;

using System.Web.Mvc;

using MvcMusicStore.Models;

using MvcMusicStore.ViewModels;

namespace MvcMusicStore.Controllers

{

public class ShoppingCartController : Controller

{

MusicStoreEntities storeDB = new MusicStoreEntities();

//

// GET: /ShoppingCart/

public ActionResult Index()

{

var cart = ShoppingCart.GetCart(this.HttpContext);

// Set up our ViewModel

var viewModel = new ShoppingCartViewModel

{

CartItems = cart.GetCartItems(),

CartTotal = cart.GetTotal()

};

// Return the view

return View(viewModel);

}

//

// GET: /Store/AddToCart/5

public ActionResult AddToCart(int id)

{

// Retrieve the album from the database

var addedAlbum = storeDB.Albums

.Single(album => album.AlbumId == id);

// Add it to the shopping cart

var cart = ShoppingCart.GetCart(this.HttpContext);

cart.AddToCart(addedAlbum);

// Go back to the main store page for more shopping

return RedirectToAction("Index");

}

//

// AJAX: /ShoppingCart/RemoveFromCart/5

[HttpPost]

public ActionResult RemoveFromCart(int id)

{

// Remove the item from the cart

var cart = ShoppingCart.GetCart(this.HttpContext);

// Get the name of the album to display confirmation

string albumName = storeDB.Carts

.Single(item => item.RecordId == id).Album.Title;

// Remove from cart

int itemCount = cart.RemoveFromCart(id);

// Display the confirmation message

var results = new ShoppingCartRemoveViewModel

{

Message = Server.HtmlEncode(albumName) +

" has been removed from your shopping cart.",

CartTotal = cart.GetTotal(),

CartCount = cart.GetCount(),

ItemCount = itemCount,

DeleteId = id

};

return Json(results);

}

//

// GET: /ShoppingCart/CartSummary

[ChildActionOnly]

public ActionResult CartSummary()

{

var cart = ShoppingCart.GetCart(this.HttpContext);

ViewData["CartCount"] = cart.GetCount();

return PartialView("CartSummary");

}

}

}

使用Ajax.ActionLink实现Ajax更新

为ShoppingCartViewModel类创建一个强类型的Shopping Cart Index页面,并且象以前一样使用List视图模板。

ASP.NET MVC Music Store教程(8):购物车和AJAX更新 - firechun - firechun的博客

(注:这里实际上不能在“支架模板”中选择List,否则生成视图后直接运行会出错。因为Index方法返回的是单个ShoppingCartViewModel对象而不是集合。当然你可以直接复制网页上的代码来覆盖自动生成的代码,不过你还是要明白这一点)

使用Ajax.ActionLink代替Html.ActionLink来生成从购物车移除物品的链接。

@Ajax.ActionLink("Remove from cart",

"RemoveFromCart",

new { id = item.RecordId }, new AjaxOptions { OnSuccess = "handleUpdate"

})

Ajax.ActionLink和Html.ActionLink方法非常相似,不同的是表单提交时为RemoveFromCart方法生成一个Ajax回调。RemoveFromCart方法返回一个序列化的JsonResult对象(关于JsonResult对象,请参见:http://msdn.microsoft.com/zh-cn/library/system.web.mvc.jsonresult.aspx) ,它被自动传递给AjaxOptions的OnSuccess参数指定的JavaScript方法,在本例中,这个方法是handleUpdate。handleUpdate使用JQuery为页面执行四个快速更新:

1.从列表中移除删掉的唱片

2.更新标题中的数量

3.为用户显示更新信息

4.更新购物车总金额

由于Index视图使用Ajax回调来处理“移除”情况,我们不需要为RemoveFromCart操作添加任何视图,完整代码如下:

@model MvcMusicStore.ViewModels.ShoppingCartViewModel

@{

ViewBag.Title = "Shopping Cart";

}

<script src="/Scripts/jquery-1.4.4.min.js" type="text/javascript"></script>

<script type="text/javascript">

$(function () {

// Document.ready -> link up remove event handler

$(".RemoveLink").click(function () {

// Get the id from the link

var recordToDelete = $(this).attr("data-id");

if (recordToDelete != ‘‘) {

// Perform the ajax post

$.post("/ShoppingCart/RemoveFromCart", { "id":

recordToDelete },

function (data) {

// Successful requests get here

// Update the page elements

if (data.ItemCount == 0) {

$(‘#row-‘ + data.DeleteId).fadeOut(‘slow‘);

} else {

$(‘#item-count-‘ +

data.DeleteId).text(data.ItemCount);

}

$(‘#cart-total‘).text(data.CartTotal);

$(‘#update-message‘).text(data.Message);

$(‘#cart-status‘).text(‘Cart (‘ + data.CartCount + ‘)‘);

});

}

});

});

function handleUpdate() {

// Load and deserialize the returned JSON data

var json = context.get_data();

var data = Sys.Serialization.JavaScriptSerializer.deserialize(json);

// Update the page elements

if (data.ItemCount == 0) {

$(‘#row-‘ + data.DeleteId).fadeOut(‘slow‘);

} else {

$(‘#item-count-‘ + data.DeleteId).text(data.ItemCount);

}

$(‘#cart-total‘).text(data.CartTotal);

$(‘#update-message‘).text(data.Message);

$(‘#cart-status‘).text(‘Cart (‘ + data.CartCount + ‘)‘);

}

</script>

<h3>

<em>Review</em> your cart:

</h3>

<p class="button">

@Html.ActionLink("Checkout

>>", "AddressAndPayment", "Checkout")

</p>

<div id="update-message">

</div>

<table>

<tr>

<th>

Album Name

</th>

<th>

Price (each)

</th>

<th>

Quantity

</th>

<th></th>

</tr>

@foreach (var item in

Model.CartItems)

{

<tr id="[email protected]">

<td>

@Html.ActionLink(item.Album.Title,

"Details", "Store", new { id = item.AlbumId }, null)

</td>

<td>

@item.Album.Price

</td>

<td id="[email protected]">

@item.Count

</td>

<td>

<a href="#" class="RemoveLink" data-id="@item.RecordId">Remove from cart</a>

</td>

</tr> //购物车

}

<tr>

<td>

Total

</td>

<td>

</td>

<td>

</td>

<td id="cart-total">

@Model.CartTotal

</td>

</tr>

</table>

要测试上面的代码,我们先更新Store Details视图,添加一个“Add to cart”按纽,我们还可以为唱片追加一些相关信息: Genre, Artist, Price, t和Album Art。更新后的Store Details视图代码如下:

@model MvcMusicStore.Models.Album

@{

ViewBag.Title = "Album - " + Model.Title;

}

<h2>@Model.Title</h2>

<p>

<img alt="@Model.Title"

src="@Model.AlbumArtUrl" />

</p>

<div id="album-details">

<p>

<em>Genre:</em>

@Model.Genre.Name

</p>

<p>

<em>Artist:</em>

@Model.Artist.Name

</p>

<p>

<em>Price:</em>

@String.Format("{0:F}",

Model.Price)

</p>

<p class="button">

@Html.ActionLink("Add to

cart", "AddToCart",

"ShoppingCart", new { id = Model.AlbumId }, "")

</p>

</div>

现在我们可以通过点击“Store”来测试对购物车添加和移除物品,运行应用程序并访问/Store:

时间: 2024-11-05 11:48:17

购物车示例的相关文章

Vue购物车实例

这是效果图 看起来很简单是不是 之前一直写Jquery代码 总是想着 DOM 操作 思维感觉没有切换过来 想了很久,最后使用Vue的属性进行控制,实现了多选计算属性的功能 直接上源码! index.html <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta http-equiv="X-UA-Compatible" content="

vue.js实战——购物车练习(包含全选功能)

vue.js实战第5章 54页的练习1 直接放代码好了,全选的部分搞了好久,代码好像有点啰嗦,好在实现功能了(*^▽^*) HTML: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-sca

vuex实践示例

前言 Vuex的文档翻了几遍,依然只会最简单的用法.对其更多的功能不太了解.这次因为重新开始一个前后端分离项目,我希望使用vue+typescript,这时就涉及到ts版的vuex,才发现自己vuex都不懂,如何ts化呢?于是再次从0开始学一遍vuex. 这次有个意外的发现,在Vuex官方文档 核心概念 - 项目结构 最后一行有句 请参考购物车示例,点击打开了github vuex项目,发现里面有很多examples.于是下载下来,一个一个学习.刚开始就是照抄代码.网上很多大神都说抄代码没用,但

走进AngularJs(一)angular基本概念的认识与实战

一.前言 前端技术的发展是如此之快,各种优秀技术.优秀框架的出现简直让人目不暇接,作为一名业界新秀,紧跟时代潮流,学习掌握新知识自然是不敢怠慢.当听到AngularJs这个名字并知道是google在维护它时,便一直在关注,看到其在国外已经十分火热,可是国内的使用情况却有不小的差距,参考文献/网络文章也很匮乏.朝思暮想良久,决定深入学习angular,并写系列博客,一方面作为自己学习路程上的记录,另一方面也给有兴趣的同学一些参考. 首先我自己是一名学习者,会以学习者的角度来整理我的行文思路,故该系

Angularjs -- 基本概念

angularjs旨在减轻使用AJAX开发应用程序的复杂度,使得程序的创建.测试.扩展和维护变得容易.下面是angularjs中的一些核心概念. 1. 客户端模板 多页面的应用通过组装和拼接服务器上的数据来生成HTML,然后输出到浏览器.Angularjs不同于此的是,传递模板和数据到浏览器,然后在浏览器端进行组装.浏览器的角色编程了只提供模板的静态资源和模板所需要的数据. hello.html <html ng-app> <head> <script src="a

《HTML 5网页开发实例详解》目录

第一篇  从宏观上认识HTML 5 讲述了HTML 5引发的Web革命.HTML 5的整体特性.HTML 5相关概念和框架和开发环境搭建. 第1章 HTML 5引发的Web革命 1.1  你是不是真的了解HTML 5 1.1.1  通过W3C认识HTML 5的发展史 1.1.2  HTML 5.HTML4.XHTML的区别 1.1.3  什么人应该学HTML 5 1.1.4  一个图告诉你如何学习HTML 5 1.2  浏览器之争 1.2.1  说说这些常见的浏览器 1.2.2  浏览器的兼容烦

Python学习记录-2016-12-18

今日学习记录: 元组: 和list的区别是,元组只有两个操作,count和index,不能修改,添加,删除 购物车示例: #!/usr/bin/env python # -*- coding: utf-8 -*- # Author:Jack Niu product_list = [     ("Iphone", 5888),     ("Mac Pro", 11000),     ("Bike", 899),     ("Book&qu

JSP编程技术5-购物车的实现-session会话对象

首先十分感谢大家对我的文章的支持,我是个刚刚才找到自己方向的在校大学生,当然我的知识和能力有限,目前正在努力恶补学习中,当我看到自己首次发表到CSDN首页的文章才几个小时阅读量就超过了100时,对我来说无疑是个莫大的鼓励,受宠若惊的我真的很感动,十分感谢大家对我的支持,不管怎样,我们都是站在巨人的肩膀上获得的一切,在社会竞争如此激烈的今天,尤其是IT界发展之迅速,要求我们每一天都需要学习很多东西才能跟上社会的节奏,我的想法就是把我所掌握的实实在在的东西通过文字的方式展现给大家,想把更多实用的东西

[转]大型 JavaScript 应用架构中的模式

目录 1.我是谁,以及我为什么写这个主题 2.可以用140个字概述这篇文章吗? 3.究竟什么是“大型”JavaScript应用程序? 4.让我们回顾一下当前的架构 5.想得长远一些 6.头脑风暴 7.架构提议 7.1 模块化理论 7.2 CommonJS模块 7.3 外观模式 7.4 中介者模式 7.5 应用外观 7.6 核心的抽象 7.7 整合 7.8 超越发布/订阅:自动注册事件 7.9 常见问题 13 August 2013 原文:Patterns For Large-Scale Java