MVC 音乐商店 第 8 部分: 购物车与 Ajax 更新
MVC 音乐商店是介绍,并分步说明了如何使用 ASP.NET MVC 和 Visual Studio 为 web 开发教程应用程序。
MVC 音乐商店是一个轻量级的示例存储实现它卖音乐专辑在线,并实现基本的网站管理、 用户登录,和购物车功能。
这个系列教程详细说明所有为构建 ASP.NET MVC 音乐商店示例应用程序采取的步骤。第 8 部分涵盖了使用 Ajax 更新购物车。
我们会允许用户在购物车中放置专辑,无需注册,但他们要先注册为客人完成结帐。购物和签出过程将分成两个控制器: 允许以匿名方式将项添加到购物车,一个购物车原型控制器和一个签出控制器处理签出过程。我们开始与购物车在这一节,然后生成下面一节中的签出过程。
添加 Cart、Order 和 OrderDetail 模型类
我们的购物车和结帐过程将使一些新的类的使用。用鼠标右键单击模型文件夹并添加一个购物车类 (Cart.cs) 用下面的代码。
usingSystem.ComponentModel.DataAnnotations;namespaceMvcMusicStore.Models{publicclassCart{[Key]publicintRecordId{get;set;}publicstringCartId{get;set;}publicintAlbumId{get;set;}publicintCount{get;set;}publicSystem.DateTimeDateCreated{get;set;}publicvirtualAlbumAlbum{get;set;}}}
此类是对他人我们使用了到目前为止,除了为 RecordId 属性 [Key] 属性很相似。我们的购物车物品将字符串标识符命名为 CartID,允许匿名购物,但表包含名为 RecordId 整数主键。通过公约 》,实体框架代码第一次预期一个名为购物车表的主键将是 CartId 或 ID,但我们可以容易地覆盖,通过注释或代码如果我们想要。这是一个示例的我们如何可以使用简单的约定框架代码第一次实体中时他们适合我们,但我们不受约束他们时他们不。
接下来,用下面的代码添加订单类 (Order.cs)。
usingSystem.Collections.Generic;namespaceMvcMusicStore.Models{publicpartialclassOrder{publicintOrderId{get;set;}publicstringUsername{get;set;}publicstringFirstName{get;set;}publicstringLastName{get;set;}publicstringAddress{get;set;}publicstringCity{get;set;}publicstringState{get;set;}publicstringPostalCode{get;set;}publicstringCountry{get;set;}publicstringPhone{get;set;}publicstringEmail{get;set;}publicdecimalTotal{get;set;}publicSystem.DateTimeOrderDate{get;set;}publicList<OrderDetail>OrderDetails{get;set;}}}
此类跟踪订单的交货和摘要信息。然而,它不会编译,因为它取决于我们尚未创建一个类明细表导航属性。让我们修复现在通过添加一个类命名 OrderDetail.cs,添加下面的代码。
namespaceMvcMusicStore.Models{publicclassOrderDetail{publicintOrderDetailId{get;set;}publicintOrderId{get;set;}publicintAlbumId{get;set;}publicintQuantity{get;set;}publicdecimalUnitPrice{get;set;}publicvirtualAlbumAlbum{get;set;}publicvirtualOrderOrder{get;set;}}}
我们会给我们的 MusicStoreEntities 类,包括 DbSets,公开这些新的模式类,还包括 DbSet < 艺术家 > 一个最后一次更新。更新后的 MusicStoreEntities 类将显示为以下。
usingSystem.Data.Entity;namespaceMvcMusicStore.Models{publicclassMusicStoreEntities:DbContext{publicDbSet<Album>Albums{get;set;}publicDbSet<Genre>Genres{get;set;}publicDbSet<Artist>Artists{get;set;}publicDbSet<Cart>Carts{get;set;}publicDbSet<Order>Orders{get;set;}publicDbSet<OrderDetail>OrderDetails{get;set;}}}
管理购物车业务逻辑
下一步,我们会在模型文件夹中创建的商城类。商城模型处理对车表的数据访问。此外,它将处理的业务逻辑的添加和移除项从购物车。
因为我们不想要求用户注册帐户只是将项目添加到购物车,我们将用户分配一个临时的唯一标识符 (使用 GUID 或全局唯一标识符) 当他们访问的购物车。我们会将存储使用 ASP.NET 会话类此 ID。
注意: ASP.NET 会话是一个方便的地方来存储用户特定的信息,将到期后他们离开网站。虽然滥用的会话状态可以有性能影响较大的站点上,我们光使用将工作以及出于演示的目的。
商城类公开下列方法:
AddToCart 用专辑作为参数,并将其添加到用户的购物车。购物车表跟踪每个专辑的数量,因为它包含逻辑,如果需要创建一个新行或只是递增数量,如果用户已经订购了专辑的一个副本。
RemoveFromCart 获取专辑 ID,并从用户的购物车中删除它。如果用户仅他们的购物车中有专辑的一个副本,则删除行。
EmptyCart从用户的购物车中移除所有项。
GetCartItems检索列表用于显示或处理的 CartItems。
GetCount检索的用户拥有自己的购物车中的专辑总数。
GetTotal计算购物车中的所有项目的总成本。
CreateOrder在签出阶段将购物车转换为顺序。
GetCart是一个静态方法,允许我们的控制器来获得购物车对象。它使用GetCartId方法来处理从该用户的会话中读取 CartId。GetCartId 方法需要 HttpContextBase,以便它可以读取用户的 CartId 从用户的会话。
这里是完整的商城类:
usingSystem;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Web;usingSystem.Web.Mvc;namespaceMvcMusicStore.Models{publicpartialclassShoppingCart{MusicStoreEntities storeDB =newMusicStoreEntities();stringShoppingCartId{get;set;}publicconststringCartSessionKey="CartId";publicstaticShoppingCartGetCart(HttpContextBase context){var cart =newShoppingCart();
cart.ShoppingCartId= cart.GetCartId(context);return cart;}// Helper method to simplify shopping cart callspublicstaticShoppingCartGetCart(Controller controller){returnGetCart(controller.HttpContext);}publicvoidAddToCart(Album album){// Get the matching cart and album instancesvar 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 =newCart{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();}publicintRemoveFromCart(int id){// Get the cartvar 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;}publicvoidEmptyCart(){var cartItems = storeDB.Carts.Where(
cart => cart.CartId==ShoppingCartId);foreach(var cartItem in cartItems){
storeDB.Carts.Remove(cartItem);}// Save changes
storeDB.SaveChanges();}publicList<Cart>GetCartItems(){return storeDB.Carts.Where(
cart => cart.CartId==ShoppingCartId).ToList();}publicintGetCount(){// Get the count of each item in the cart and sum them upint? count =(from cartItems in storeDB.Cartswhere cartItems.CartId==ShoppingCartIdselect(int?)cartItems.Count).Sum();// Return 0 if all entries are nullreturn count ??0;}publicdecimalGetTotal(){// 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 totaldecimal? total =(from cartItems in storeDB.Cartswhere cartItems.CartId==ShoppingCartIdselect(int?)cartItems.Count*
cartItems.Album.Price).Sum();return total ??decimal.Zero;}publicintCreateOrder(Order order){decimal orderTotal =0;var cartItems =GetCartItems();// Iterate over the items in the cart, // adding the order details for eachforeach(var item in cartItems){var orderDetail =newOrderDetail{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); storeDB.OrderDetails.Add(orderDetail);}// Set the order's total to the orderTotal count
order.Total= orderTotal;// Save the order
storeDB.SaveChanges();// Empty the shopping cartEmptyCart();// Return the OrderId as the confirmation numberreturn order.OrderId;}// We're using HttpContextBase to allow access to cookies.publicstringGetCartId(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 classGuid 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 usernamepublicvoidMigrateCart(string userName){var shoppingCart = storeDB.Carts.Where(
c => c.CartId==ShoppingCartId);foreach(Cart item in shoppingCart){
item.CartId= userName;}
storeDB.SaveChanges();}}}
ViewModels
我们购物的购物车控制器将需要一些复杂信息传达给它的看法,不干净地映射到我们的模型对象。我们不想修改我们的模型以适应我们的意见 ;模型的类应表示我们的域,不是用户界面。一个解决办法是信息的将信息传递到我们使用 ViewBag 类,因为我们做了与存储管理器下拉列表的信息,但通过 ViewBag 传递大量获取难管理的意见。
问题的解决办法是使用ViewModel模式。使用此模式时我们创建强类型化的类,优化我们的特定视图的情况下,和其中公开我们的视图模板所需要的动态值/内容的属性。我们的控制器类,然后填充并将这些视图优化类传递给我们要使用的视图模板。这使类型安全、 编译时检查和内查看模板编辑器智能感知。
我们会在我们的购物车控制器中创建使用的两种视图模型: ShoppingCartViewModel 将保存用户的购物车的内容和 ShoppingCartRemoveViewModel 将用于在用户删除的东西从他们车时显示确认信息。
让我们在以保持组织的事情我们项目的根目录中创建一个新的 ViewModels 文件夹。右键单击项目,选择添加 / 新的文件夹。
ViewModels 文件夹的名称。
接下来,添加 ViewModels 文件夹中的 ShoppingCartViewModel 类。它具有两个属性: 购物车物品和在购物车中保存的所有项目的总价格的十进制数值的列表。
usingSystem.Collections.Generic;usingMvcMusicStore.Models;namespaceMvcMusicStore.ViewModels{publicclassShoppingCartViewModel{publicList<Cart>CartItems{get;set;}publicdecimalCartTotal{get;set;}}}
现在将 ShoppingCartRemoveViewModel 添加到 ViewModels 文件夹中,具有以下四个属性。
namespaceMvcMusicStore.ViewModels{publicclassShoppingCartRemoveViewModel{publicstringMessage{get;set;}publicdecimalCartTotal{get;set;}publicintCartCount{get;set;}publicintItemCount{get;set;}publicintDeleteId{get;set;}}}
购物车控制器
购物车控制器有三个主要目的: 将项添加到购物车、 购物车,从删除项目和查看购物车中的项目。它将使三个类的使用我们刚刚创建: ShoppingCartViewModel,ShoppingCartRemoveViewModel 和商城。在 StoreController 和 StoreManagerController,我们将添加一个字段来保存 MusicStoreEntities 的实例。
将一种新的购物车控制器添加到使用空的控制器模板的项目。
这里是完整的商城控制器。索引和添加控制器操作应该看起来很熟悉。删除和 CartSummary 的控制器操作处理两种特殊情况下,我们会在下面一节中讨论。
usingSystem.Linq;usingSystem.Web.Mvc;usingMvcMusicStore.Models;usingMvcMusicStore.ViewModels;namespaceMvcMusicStore.Controllers{publicclassShoppingCartController:Controller{MusicStoreEntities storeDB =newMusicStoreEntities();//// GET: /ShoppingCart/publicActionResultIndex(){var cart =ShoppingCart.GetCart(this.HttpContext);// Set up our ViewModelvar viewModel =newShoppingCartViewModel{CartItems= cart.GetCartItems(),CartTotal= cart.GetTotal()};// Return the viewreturnView(viewModel);}//// GET: /Store/AddToCart/5publicActionResultAddToCart(int id){// Retrieve the album from the databasevar addedAlbum = storeDB.Albums.Single(album => album.AlbumId== id);// Add it to the shopping cartvar cart =ShoppingCart.GetCart(this.HttpContext); cart.AddToCart(addedAlbum);// Go back to the main store page for more shoppingreturnRedirectToAction("Index");}//// AJAX: /ShoppingCart/RemoveFromCart/5[HttpPost]publicActionResultRemoveFromCart(int id){// Remove the item from the cartvar cart =ShoppingCart.GetCart(this.HttpContext);// Get the name of the album to display confirmationstring albumName = storeDB.Carts.Single(item => item.RecordId== id).Album.Title;// Remove from cartint itemCount = cart.RemoveFromCart(id);// Display the confirmation messagevar results =newShoppingCartRemoveViewModel{Message=Server.HtmlEncode(albumName)+" has been removed from your shopping cart.",CartTotal= cart.GetTotal(),CartCount= cart.GetCount(),ItemCount= itemCount,DeleteId= id
};returnJson(results);}//// GET: /ShoppingCart/CartSummary[ChildActionOnly]publicActionResultCartSummary(){var cart =ShoppingCart.GetCart(this.HttpContext);ViewData["CartCount"]= cart.GetCount();returnPartialView("CartSummary");}}}
与 jQuery 的 Ajax 更新
我们下一步就会创建一个购物车索引页,强类型化为 ShoppingCartViewModel 并使用列表视图模板使用相同的方法。
但是,没有使用 Html.ActionLink 来从购物车中删除项目,我们会使用 jQuery 以"丝"在此视图中的所有链接,有 HTML 类 RemoveLink 的 click 事件。而不是发布窗体,此 click 事件处理程序将只是对我们的 RemoveFromCart 控制器操作进行 AJAX 回调。RemoveFromCart 返回 JSON 序列化的结果,其中我们 jQuery 回调然后将分析和执行四个快速更新到使用 jQuery 的页:
- 1.从列表中移除已删除的专辑
- 2.更新标头中的购物车计数
- 3.向用户显示更新消息
- 4.更新购物车总价格
因为删除方案由索引视图中的 Ajax 回调正在处理,我们不需要附加视图 RemoveFromCart 采取行动。下面是完整的代码为 /ShoppingCart/Index 视图:
@model MvcMusicStore.ViewModels.ShoppingCartViewModel
@{
ViewBag.Title = "Shopping Cart";
}
<scriptsrc="/Scripts/jquery-1.4.4.min.js"type="text/javascript"></script><scripttype="text/javascript">
$(function(){// Document.ready -> link up remove event handler
$(".RemoveLink").click(function(){// Get the id from the linkvar 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 elementsif(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><pclass="button">
@Html.ActionLink("Checkout
>>", "AddressAndPayment", "Checkout")
</p><divid="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)
{
<trid="row-@item.RecordId"><td>
@Html.ActionLink(item.Album.Title,
"Details", "Store", new { id = item.AlbumId }, null)
</td><td>
@item.Album.Price
</td><tdid="item-count-@item.RecordId">
@item.Count
</td><td><ahref="#"class="RemoveLink"data-id="@item.RecordId">Remove
from cart</a></td></tr>
}
<tr><td>
Total
</td><td></td><td></td><tdid="cart-total">
@Model.CartTotal
</td></tr></table>
若要测试这一点,我们需要能够将项目添加到我们的购物车。我们会更新我们存储的详细信息视图以包括"添加到购物车"按钮。虽然我们在它,我们可以包含的一些专辑附加信息,我们已经添加了因为我们最后更新此视图: 流派、 艺术家、 价格和唱片集画面。更新的存储详细信息视图代码将显示如下所示。
@model MvcMusicStore.Models.Album
@{
ViewBag.Title = "Album - " + Model.Title;
}
<h2>@Model.Title</h2><p><imgalt="@Model.Title"src="@Model.AlbumArtUrl"/></p><divid="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><pclass="button">
@Html.ActionLink("Add to
cart", "AddToCart",
"ShoppingCart", new { id = Model.AlbumId }, "")
</p></div>
现在我们可以单击通过商店和测试添加和删除专辑,并从我们的购物车。运行该应用程序并浏览到存储索引。
下一步,单击一种类型来查看的相册列表。
现在单击唱片集标题显示我们更新唱片集详细信息视图中,包括"添加到购物车"按钮。
单击"添加到购物车"按钮显示我们购物的购物车索引视图中,购物车摘要列表。
在加载到您的购物车之后, 你可以点击从购物车链接删除,请参见 Ajax 更新到您的购物车。
我们构建了购物车,允许未注册的用户将项添加到购物车工作。在以下部分中,我们会让他们注册并完成结帐过程。
MVC 音乐商店 第 8 部分: 购物车与 Ajax 更新的更多相关文章
- MVC 音乐商店 第 9 部分: 注册和结帐
MVC 音乐商店是介绍,并分步说明了如何使用 ASP.NET MVC 和 Visual Studio 为 web 开发教程应用程序. MVC 音乐商店是一个轻量级的示例存储实现它卖音乐专辑在线,并实现 ...
- ASP.NET MVC 音乐商店 - 目录
这一个系列的内容来自微软的音乐商店 Music Store, 这是项目在 Codeplex 上的地址:http://mvcmusicstore.codeplex.com/. 这个项目使用 ASP.NE ...
- ASP.NET MVC 音乐商店 - 3. 视图与模型
上一篇中使用字符串,这一篇我们就开始使用视图来处理. 我们已经可以从控制器的 Action 中返回一个字符串,这可以帮助我们更好地理解 Controller 是如何工作的.但是对于创建一个 Web 程 ...
- ASP.NET MVC 音乐商店 - 0 概览
这是一个系列文章,原文内容出自微软的 MusicStore. 首先对原文内容进行了简单的翻译,以方便大家参考,另外对于其中的部分内容,也进行了简单的分析,使用的 Visual Studio 也换成了中 ...
- Mvc音乐商店demo的ajax异步删除功能总结
刚刚从学校出来参加工作,没啥工作经验,所以各位大神们不要嘲笑哈! 来公司后要进行培训,给我们的作业中有一个使用 dapper+mvc+ajax+SQL Server 2008,来实现一个音乐商店的de ...
- ASP.NET MVC 音乐商店 - 2.控制器
在典型的 Web 应用中,用户请求的 URL 地址通常映射到保存在网站中的文件上,例如,当用户请求 /Products.aspx 的时候,或者 /Products.php 的时候,很可能是在通过处理 ...
- ASP.NET MVC 音乐商店 - 6. 使用 DataAnnotations 进行模型验证
在前面的创建专辑与编辑专辑的表单中存在一个问题:我们没有进行任何验证.字段的内容可以不输入,或者在价格的字段中输入一些字符,在执行程序的时候,这些错误会导致数据库保存过程中出现错误,我们将会看到来自数 ...
- ASP.NET MVC 音乐商店 - 5 通过支架创建编辑表单 续
查看 StoreManager 控制器的代码 现在,Store Manager 控制器中已经包含了一定数量的代码,我们从头到尾重新过一下. 首先,在控制器中包含了标准的 MVC 控制器的代码,为了使用 ...
- ASP.NET MVC 音乐商店 - 5. 通过支架创建编辑表单
在上一章,我们已经从数据库获取数据,然后显示出来,这一章,我们将允许编辑数据. 创建 StoreManagerController 控制器 我们将要创建称为 StoreManager 的控制器,对于这 ...
随机推荐
- LinkedHashMap和HashMap的比较使用(转载)
LinkedHashMap和HashMap的比较使用 ? import java.util.HashMap; import java.util.Iterator; import java.util.L ...
- uboot的mkconfig分析
uboot的mkconfig是一个shell脚本.对于笔者这种Linux学习初学者,不太可能认真的把shell脚本学习一遍.但是,倘若不能理解mkconfig的含义,又很难从整体的理解uboot(我认 ...
- android SurfaceView绘制 重新学习--控制动画移动
直接上demo,图是自己切的,将就用吧.点击左右两边分别向左右移动. public class MySurfaceView extends SurfaceView implements Callbac ...
- YUV数据格式
概要: 与RGB编码方法类似,YUV也是一种颜色编码方法,主要用于电视系统以及模拟视频领域,它是指将亮度参量(Y:Luminance或Luma)和色度参量(UV:Chrominance或Chroma) ...
- Android 程序框架设计
这篇文章主要内容来自于之前我讲的一个PPT文档,现在将其整理如下.欢迎指正.以下的内容都是来自于我自身的经验,欢迎大家多提自己的建议. 1.一些概念 模式的定义: 每个模式都描述了一个在我们的环境中不 ...
- UVA 11426 GCD Extrme (Ⅲ)
给定一个整数N(1<N<=4000000)的整数求∑GCD(i,j)i=1,2,3....j-1,2<=j<=n的值.参考了一下网上的题解,复述一下我理解后的思路,加深理解: ...
- [wikioi]石子归并
http://wikioi.com/problem/1048/ 区间型动态规划.参考PPT:http://wenku.baidu.com/view/73c1ded5b9f3f90f76c61bc4.h ...
- Https协议:SSL建立过程分析(也比较清楚,而且有OpenSSL的代码)
web访问的两种方式: http协议,我们一般情况下是通过它访问web,因为它不要求太多的安全机制,使用起来也简单,很多web站点也只支持这种方式下的访问. https协议(Hypertext Tra ...
- VC提交网页表单(一共八篇)
VC提交网页表单-自动评论留言(1)http://blog.csdn.net/wangningyu/article/details/4526357VC提交网页表单-自动评论留言(2)http://bl ...
- android利用剪切板来实现数据的传递
在Android开发中我们经常要遇到的一个问题就是数据在不同的Activity之间的共享.在Android开发中有很多种方法可以达到这个目地. 这里介绍一种比较常见.又常用的一种方法就是使用剪切板.我 ...