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;}publicListOrderDetails{ 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{ publicDbSetAlbums{ get;set;}publicDbSet Genres{ get;set;}publicDbSet Artists{ get;set;}publicDbSet Carts{ get;set;}publicDbSet Orders{ get;set;}publicDbSet 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();}publicListGetCartItems(){ 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{ publicListCartItems{ 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";}$(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+')');});}});}); Review your cart:
@Html.ActionLink("Checkout>>", "AddressAndPayment", "Checkout") @item.Count @Model.CartTotal
@foreach (var item inModel.CartItems) { Album Name Price (each) Quantity } @Html.ActionLink(item.Album.Title,"Details", "Store", new { id = item.AlbumId }, null) @item.Album.Price Removefrom cart Total
若要测试这一点,我们需要能够将项目添加到我们的购物车。我们会更新我们存储的详细信息视图以包括"添加到购物车"按钮。虽然我们在它,我们可以包含的一些专辑附加信息,我们已经添加了因为我们最后更新此视图: 流派、 艺术家、 价格和唱片集画面。更新的存储详细信息视图代码将显示如下所示。
@model MvcMusicStore.Models.Album@{ ViewBag.Title = "Album - " + Model.Title; }@Model.Title
Genre: @Model.Genre.Name
Artist: @Model.Artist.Name
Price: @String.Format("{0:F}",Model.Price)
@Html.ActionLink("Add tocart", "AddToCart", "ShoppingCart", new { id = Model.AlbumId }, "")
现在我们可以单击通过商店和测试添加和删除专辑,并从我们的购物车。运行该应用程序并浏览到存储索引。
下一步,单击一种类型来查看的相册列表。
现在单击唱片集标题显示我们更新唱片集详细信息视图中,包括"添加到购物车"按钮。
单击"添加到购物车"按钮显示我们购物的购物车索引视图中,购物车摘要列表。
在加载到您的购物车之后, 你可以点击从购物车链接删除,请参见 Ajax 更新到您的购物车。
我们构建了购物车,允许未注册的用户将项添加到购物车工作。在以下部分中,我们会让他们注册并完成结帐过程。