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

摘要:

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

猜你喜欢

转载自www.cnblogs.com/uncle_danny/p/9052938.html
今日推荐