码迷,mamicode.com
首页 > Web开发 > 详细

跟我学ASP.NET MVC之七:SportsStrore购物车

时间:2018-05-18 20:38:46      阅读:233      评论:0      收藏:0      [点我收藏+]

标签:属性   NPU   string   str   label   pap   不能   default   warning   

摘要:

SportsStore应用程序进展很顺利,但是我不能销售产品直到设计了一个购物车。在这篇文章里,我就将创建一个购物车。

在目录下的每个产品旁边添加一个添加到购物车按钮。点击这个按钮将显示客户到目前为止选择的产品摘要,包含总价格。这时候,用户可以点击继续购物按钮返回产品目录,或者点击现在下单按钮完成订单结束购物过程。

定义Cart实体类

在SportsStore.Domain工程的Entities文件夹下,创建代码文件Cart.cs。

 1 using System.Collections.Generic;
 2 using System.Linq;
 3 
 4 namespace SportsStore.Domain.Entities
 5 {
 6     public class Cart
 7     {
 8         private List<CartLine> lineCollection = new List<CartLine>();
 9         public void AddItem(Product product, int quantity)
10         {
11             CartLine line = lineCollection.Where(p => p.Product.ProductID == product.ProductID).FirstOrDefault();
12             if (line == null)
13             {
14                 lineCollection.Add(new CartLine
15                 {
16                     Product = product,
17                     Quantity = quantity
18                 });
19             }
20             else {
21                 line.Quantity += quantity;
22             }
23         }
24 
25         public void RemoveLine(Product product)
26         {
27             lineCollection.RemoveAll(l => l.Product.ProductID == product.ProductID);
28         }
29 
30         public decimal ComputeTotalValue()
31         {
32             return lineCollection.Sum(e => e.Product.Price * e.Quantity);
33         }
34 
35         public void Clear()
36         {
37             lineCollection.Clear();
38         }
39 
40         public IEnumerable<CartLine> CartLines
41         {
42             get { return lineCollection; }
43         }
44     }
45 
46     public class CartLine
47     {
48         public Product Product { get; set; }
49         public int Quantity { get; set; }
50     }
51 }

Cart类使用了CartLine类,他们定义在同一个代码文件内,保存一个客户选择的产品,以及客户想买的数量。我定义了添加条目到购物车的方法,从购物车删除之前已经添加的条目的方法,计算购物车内条目总价格,以及删除所有条目清空购物车的方法。我还提供了一个通过IEnumrable<CartLine>访问购物车内容的属性。这些都很直观,通过一点点LINQ很容易用C#实施。

定义视图模型类

在SportsStore.WebUI工程的Models文件夹内,创建代码文件CartIndexViewModel。

 1 using SportsStore.Domain.Entities;
 2 
 3 namespace SportsStore.WebUI.Models
 4 {
 5     public class CartIndexViewModel
 6     {
 7         public Cart Cart { get; set; }
 8         public string ReturnUrl { get; set; }
 9     }
10 }

该模型类有两个属性。Cart属性保存了购物车信息,ReturnUrl保存了产品目录的URL,需要这个信息是因为,客户可以随时点击继续购物按钮,返回之前的产品目录URL。

添加购物车控制器CartController

 1 using SportsStore.Domain.Abstract;
 2 using SportsStore.Domain.Entities;
 3 using SportsStore.WebUI.Models;
 4 using System.Linq;
 5 using System.Web.Mvc;
 6 
 7 namespace SportsStore.WebUI.Controllers
 8 {
 9     public class CartController : Controller
10     {
11         private IProductRepository repository;
12 
13         public CartController(IProductRepository productRepository)
14         {
15             repository = productRepository;
16         }
17 
18         public ActionResult Index(string returnUrl)
19         {
20             return View(new CartIndexViewModel
21             {
22                 Cart = GetCart(),
23                 ReturnUrl = returnUrl
24             });
25         }
26 
27         public RedirectToRouteResult AddToCart(int productId, string returnUrl)
28         {
29             Product product = repository.Products.FirstOrDefault(p => p.ProductID == productId);
30             if (product != null)
31             {
32                 GetCart().AddItem(product, 1);
33             }
34             return RedirectToAction("Index", new { returnUrl = returnUrl });
35         }
36 
37         public RedirectToRouteResult RemoveFromCart(int productId, string returnUrl)
38         {
39             Product product = repository.Products.FirstOrDefault(p => p.ProductID == productId);
40             if (product != null)
41             {
42                 GetCart().RemoveLine(product);
43             }
44             return RedirectToAction("Index", new { returnUrl });
45         }
46 
47         private Cart GetCart()
48         {
49             Cart cart = (Cart)Session["Cart"];
50             if (cart == null)
51             {
52                 cart = new Cart();
53                 Session["Cart"] = cart;
54             }
55             
56            return cart;
57         }
58     }
59 }

该控制器的一些解释:

  • GetCart方法:从Session里获取购物车对象,如果该对象为空,则创建这个对象,添加到Session,并返回该对象。
  • Index方法:传入returnUrl参数,返回购物车摘要信息视图。该视图的模型类是CartIndexViewModel,模型类对象的Cart属性通过调用方法GetCart返回,ReturnUrl属性使用方法参数赋值。
  • AddToCart方法:传入productId参数和returnUrl参数,添加产品到购物车,并返回重定向的购物车摘要信息视图。方法的返回类型是RedirectToRouteResult,该类的基类是ActionResult。
  • RemoveFromCart方法:传入productId参数和returnUrl参数,从购物车中删除产品,并返回重定向的购物车摘要信息视图。
  • AddToCart方法和RemoveFromCart方法都是通过调用Controller基类的RedirectToAction方法,返回重定向视图类RedirectToRouteResult的对象。
  • RedirectToAction方法的第一个参数是Action名称,第二个无类型对象参数提供传入Action的参数值。这里将重定向到Cart控制器的Index方法。

添加到购物车按钮

修改ProductSummary.cshtml视图,添加Add to Cart按钮。

 1 @model SportsStore.Domain.Entities.Product
 2 
 3 <div class="well">
 4     <h3>
 5         <strong>@Model.Name</strong>
 6         <span class="pull-right label label-primary">@Model.Price.ToString("c")</span>
 7     </h3>
 8     @using (Html.BeginForm("AddToCart", "Cart"))
 9     {
10         <div class="pull-right">
11             @Html.HiddenFor(x => x.ProductID)
12             @Html.Hidden("returnUrl", Request.Url.PathAndQuery)
13             <input type="submit" class="btn btn-success" value="Add to cart" />
14         </div>
15     }
16     <span class="lead"> @Model.Description</span>
17 </div>
  • 使用Html.BeginForm帮助方法,生成AddToCart表单。方法的第一个参数是Action名称AddToCart,第二个参数是控制器名称Cart。
  • 使用Html.HiddenFor帮助方法,生成表单的hidden html元素,该元素的name属性是字符串ProductID,值是该产品的ProductID值。
  • 使用Html.Hidden帮助方法,生成表单的hidden html元素,该元素的name属性是字符串returnUrl,值是当前页面的Url。
  • 控制器的AddToCart方法将通过表单元素的名称,获取要传入该方法的参数productID和returnUrl的值(大小写不敏感)。

添加购物车详细信息视图

在Views文件夹的Cart文件夹内,添加Index.cshtml。

 1 @model SportsStore.WebUI.Models.CartIndexViewModel
 2 
 3 @{
 4     ViewBag.Title = "Sports Store: Your Cart";
 5 }
 6 <style>
 7     #cartTable td {
 8         vertical-align: middle;
 9     }
10 </style>
11 <h2>Your cart</h2>
12 <table id="cartTable" class="table">
13     <thead>
14         <tr>
15             <th>Quantity</th>
16             <th>Item</th>
17             <th class="text-right">Price</th>
18             <th class="text-right">Subtotal</th>
19         </tr>
20     </thead>
21     <tbody>
22         @foreach (var line in Model.Cart.CartLines)
23         {
24             <tr>
25                 <td class="text-center">@line.Quantity</td>
26                 <td class="text-left">@line.Product.Name</td>
27                 <td class="text-right">
28                     @line.Product.Price.ToString("c")
29                 </td>
30                 <td class="text-right">
31                     @((line.Quantity * line.Product.Price).ToString("c"))
32                 </td>
33                 <td>
34                     @using (Html.BeginForm("RemoveFromCart", "Cart"))
35                     {
36                         @Html.Hidden("ProductId", line.Product.ProductID)
37                         @Html.HiddenFor(x => x.ReturnUrl)
38                         <input class="btn btn-sm btn-warning" type="submit" value="Remove" />
39                     }
40                 </td>
41             </tr>
42         }
43     </tbody>
44     <tfoot>
45         <tr>
46             <td colspan="3" class="text-right">Total:</td>
47             <td class="text-right">
48                 @Model.Cart.ComputeTotalValue().ToString("c")
49             </td>
50         </tr>
51     </tfoot>
52 </table>
53 <div class="text-center">
54     <a class="btn btn-primary" href="@Model.ReturnUrl">Continue shopping</a>
55 </div>
  • 这个视图以表格的形式,展示了购物车摘要产品信息,包含了产品名称、购买数量、单价、价格信息。
  • 每个产品条目后面,添加删除表单和删除按钮,这里的表单和按钮,同之前添加到购物车按钮一样。
  • 表格底部,调用ComputeTotalValue方法,返回总价格。
  • 页面底部中间,显示一个Continue Shopping按钮,ReturnUrl属性指向之前的产品目录Url,点击后返回产品目录页面。

运行程序,得到运行结果。

技术分享图片

这里我选择了Chess目录,浏览器地址栏上的URL变成了:http://localhost:17596/Chess

如果我点击Human Chess Board产品的Add To Cart按钮,得到页面:

技术分享图片

注意这时候的浏览器地址栏的地址变成了:http://localhost:17596/Cart/Index?returnUrl=%2FChess,包含的购物车的Cart/Index,以及以问号?开始的参数?returnUrl=%2FChess。returnUrl的值就是刚才的页面地址。

如果再点击Continue Shoppinga按钮,将返回到returnUrl指向的页面,既是刚才的页面:http://localhost:17596/Chess

添加购物车摘要视图

我还需要添加一个显示购物车摘要信息的小部件,可以在所有应用程序页面上都能看到,点击后返回购物车详细信息。这个小部件和导航条目类似,需要使用返回PartialViewResult的Action方法,在_Layout.cshtml视图中,使用Html.Action方法嵌入这个视图。

首先修改CartController控制器,添加Summary方法。

1         public PartialViewResult Summary()
2         {
3             return PartialView(GetCart());
4         }

然后,添加Summary视图。

1 @model SportsStore.Domain.Entities.Cart
2 
3 <div class="navbar-text navbar-right">
4     <b>Your cart:</b>
5     @Model.CartLines.Sum(x => x.Quantity) item(s),
6     @Model.ComputeTotalValue().ToString("c")
7 </div>

最后,修改_Layout.cshtml文件,调用Html帮助类方法Action,嵌入这个视图到头部导航栏内。

 1 <!DOCTYPE html>
 2 
 3 <html>
 4 <head>
 5     <meta name="viewport" content="width=device-width, initial-scale=1.0">
 6     <link href="~/Content/bootstrap.css" rel="stylesheet" />
 7     <link href="~/Content/bootstrap-theme.css" rel="stylesheet" />
 8     <title>@ViewBag.Title</title>
 9     <style>
10         .navbar-right {
11             float: right !important;
12             margin-right: 15px;
13             margin-left: 15px;
14         }
15     </style>
16 </head>
17 <body>
18     <div class="navbar navbar-inverse" role="navigation">
19         <a class="navbar-brand" href="#">SPORTS STORE</a>
20         @Html.Action("Summary", "Cart")
21     </div>
22     <div class="row panel">
23         <div id="categories" class="col-xs-3">
24             @Html.Action("Menu", "Nav")
25         </div>
26         <div class="col-xs-8">
27             @RenderBody()
28         </div>
29     </div>
30 </body>
31 </html>

这里添加页面样式navbar-right,使得购物车摘要信息部件在头部导航栏内靠右显示。

运行程序,得到运行结果。

技术分享图片

 使用模板绑定

MVC使用一个名叫模板绑定的系统,为了传参数给行为方法,它从HTTP请求创建C#对象并作为参数传给行为方法。MVC框架就是这样来处理表单的。它看到目标行为方法的参数,然后使用模板绑定得到浏览器发送过来的表单里元素的值,然后根据名称转化成对应类型的相同名称的参数,传给行为方法。

模板绑定可以从请求里的任何信息中创建C#类型。这是MVC框架的核心特征之一。我将创建一个客户的模板绑定来改进CartController控制器。

在SportsStore.WebUI工程的Infrastructure文件夹内创建子文件夹Binders,并在子文件夹下创建代码文件CartModelBinder.cs。

 1 using SportsStore.Domain.Entities;
 2 using System.Web.Mvc;
 3 
 4 namespace SportsStore.WebUI.Infrastructure.Binders
 5 {
 6     public class CartModelBinder : IModelBinder
 7     {
 8         private const string sessionKey = "Cart";
 9 
10         public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
11         {
12             // get the Cart from the session
13             Cart cart = null;
14             if (controllerContext.HttpContext.Session != null)
15             {
16                 cart = (Cart)controllerContext.HttpContext.Session[sessionKey];
17             }
18             // create the Cart if there wasn‘t one in the session data
19             if (cart == null)
20             {
21                 cart = new Cart();
22                 if (controllerContext.HttpContext.Session != null)
23                 {
24                     controllerContext.HttpContext.Session[sessionKey] = cart;
25                 }
26             }
27             // return the cart
28             return cart;
29         }
30     }
31 }
  • CartModelBinder类继承接口IModelBinder,并实现接口的方法BindModel。
  • 接口方法BindModel提供两个参数,参数类型ControllerContext:controllerContext获取控制器上下文环境信息,参数类型ModelBindingContext:bindingContext获取绑定的上下文信息。
  • 接口方法BindModel返回类型是object,他返回的对象的值就是Action方法参数的值。
  • 参数controllerContext对象的HttpContext属性保存了HTTP请求中的信息,通过controllerContext.HttpContext.Session获取HTTP请求中的Session信息。
  • 我的BindMode方法的方法体代码和CartController控制器的GetCart方法相同。都是从Session里获取购物车对象,如果该对象为空,则创建这个对象,添加到Session,并返回该对象。

有了模板绑定方法后,还需要将模板方法在Global.asax.cs代码的事件Application_Start内,通过调用ModelBinders.Binders.Add方法,注册到MVC应用程序里。

修改Global.asax代码。

 1 using SportsStore.Domain.Entities;
 2 using SportsStore.WebUI.Infrastructure.Binders;
 3 using System;
 4 using System.Collections.Generic;
 5 using System.Linq;
 6 using System.Web;
 7 using System.Web.Mvc;
 8 using System.Web.Routing;
 9 
10 namespace SportsStore
11 {
12     public class MvcApplication : System.Web.HttpApplication
13     {
14         protected void Application_Start()
15         {
16             AreaRegistration.RegisterAllAreas();
17             RouteConfig.RegisterRoutes(RouteTable.Routes);
18 
19             ModelBinders.Binders.Add(typeof(Cart), new CartModelBinder());
20         }
21     }
22 }
ModelBinders.Binders是ModelBinders的静态属性,它是一个继承自IDictionary类型的对象,它的Add方法提供两个参数完成模板绑定类型的绑定。第一个参数是返回类型参数,第二个参数实例化一个继承自IModelBinder类型的对象。
这样,我现在可以修改CartController控制器的各个Action方法,添加Cart类型参数,并使用模板绑定方式获得Cart对象参数的值。
 1 using SportsStore.Domain.Abstract;
 2 using SportsStore.Domain.Entities;
 3 using SportsStore.WebUI.Models;
 4 using System.Linq;
 5 using System.Web.Mvc;
 6 
 7 namespace SportsStore.WebUI.Controllers
 8 {
 9     public class CartController : Controller
10     {
11         private IProductRepository repository;
12 
13         public CartController(IProductRepository productRepository)
14         {
15             repository = productRepository;
16         }
17 
18         public ActionResult Index(Cart cart, string returnUrl)
19         {
20             return View(new CartIndexViewModel
21             {
22                 Cart = cart,
23                 ReturnUrl = returnUrl
24             });
25         }
26 
27         public RedirectToRouteResult AddToCart(Cart cart, int productId, string returnUrl)
28         {
29             Product product = repository.Products.FirstOrDefault(p => p.ProductID == productId);
30             if (product != null)
31             {
32                 cart.AddItem(product, 1);
33             }
34             return RedirectToAction("Index", new { returnUrl = returnUrl });
35         }
36 
37         public RedirectToRouteResult RemoveFromCart(Cart cart, int productId, string returnUrl)
38         {
39             Product product = repository.Products.FirstOrDefault(p => p.ProductID == productId);
40             if (product != null)
41             {
42                 cart.RemoveLine(product);
43             }
44             return RedirectToAction("Index", new { returnUrl });
45         }
46 
47         public PartialViewResult Summary(Cart cart)
48         {
49             return PartialView(cart);
50         }
51     }
52 }

控制器方法的参数Cart:cart将从模板绑定类的方法BindModel中,返回cart对象。

 



 

跟我学ASP.NET MVC之七:SportsStrore购物车

标签:属性   NPU   string   str   label   pap   不能   default   warning   

原文地址:https://www.cnblogs.com/uncle_danny/p/9052938.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!