• MVC项目实践,在三层架构下实现SportsStore-06,实现购物车


    SportsStore是《精通ASP.NET MVC3框架(第三版)》中演示的MVC项目,在该项目中涵盖了MVC的众多方面,包括:使用DI容器、URL优化、导航、分页、购物车、订单、产品管理、图像上传......是不错的MVC实践项目,但该项目不是放在多层框架下开发的,离真实项目还有一段距离。本系列将尝试在多层框架下实现SportsStore项目,并用自己的方式实现一些功能。

    本篇为系列第六篇,包括:

    ■ 8、购物车
        □ 8.1 购物车模型 购物车帮助类
        □ 8.2 添加"放入购物车"按钮
        □ 8.3 显示购物车内容 添加"移除"按钮
        □ 8.4 显示购物车摘要
       

      8、购物车

      8.1 购物车模型 购物车帮助类

    从逻辑上讲,购物车帮助类不仅仅是针对Product的处理,还需考虑每种Product的数量。我们把这2个因素综合起来封装成一个基本单元:

    public class CartLine
        {
            public Product Product { get; set; }
            public int Quantity { get; set; }
        }

    而购物车帮助类的主要工作就是针对这个CartLine集合的各种处理:添加、移除、计算总价、清空等。

    using System.Collections.Generic;
    using System.Linq;
    using MySportsStore.Model;
    
    namespace MySportsStore.WebUI.Models
    {
        public class Cart
        {
            private List<CartLine> lineCollection = new List<CartLine>();
    
            //添加
            public void AddItem(Product product, int quantity)
            {
                CartLine line = lineCollection.Where(p => p.Product.Id == product.Id).FirstOrDefault();
                if (line == null)
                {
                    lineCollection.Add(new CartLine(){Product = product, Quantity = quantity});
                }
                else
                {
                    line.Quantity += quantity;
                }
            }
    
            //移除
            public void RemoveLine(Product product)
            {
                lineCollection.RemoveAll(p => p.Product.Id == product.Id);
            }
    
            //计算总价
            public decimal ComputeTotalValue()
            {
                return lineCollection.Sum(p => p.Product.Price*p.Quantity);
            }
    
            //清空
            public void Clear()
            {
                lineCollection.Clear();
            }
    
            //获取
            public IEnumerable<CartLine> Lines
            {
                get { return lineCollection; }
            }
        }
    }

      8.2 添加"放入购物车"按钮

    在显示产品的部分视图中,添加"放入购物车"按钮,把Produect的Id和当前页的URL作为隐藏域传递给控制器方法:

    @model MySportsStore.Model.Product
    
    <div class="item">
        <h3>@Model.Name</h3>
        @Model.Description
        
       @using (Html.BeginForm("AddToCart", "Cart"))
       {
           @Html.HiddenFor(p => p.Id)
           @Html.Hidden("returnUrl", Request.Url.PathAndQuery)
           <input type="submit" value="+放入购物车"/>
       }
    
        <h4>@Model.Price.ToString("c")</h4>
    </div>

    在即将创建的Cart控制器方法中,肯定要用到Cart类的实例,而该实例将贯穿于页面之间,所以应该存放于Session中。首先想到的是通过如下方式获取Cart类的实例:

    private Cart GetCart()
            {
                Cart cart = (Cart)Session["Cart"];
                if (cart == null)
                {
                    cart = new Cart();
                    Session["Cart"] = cart;
                }
                return cart;
            }

    也可以自定义一个ModelBinder,使之从Session中获取Cart类的实例:

    using System.Web.Mvc;
    using MySportsStore.WebUI.Models;
    
    namespace MySportsStore.WebUI.Extension
    {
        public class CartModelBinder : IModelBinder
        {
            private const string sessionKey = "Cart";
            public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
            {
                Cart cart = (Cart)controllerContext.HttpContext.Session[sessionKey];
                if (cart == null)
                {
                    cart = new Cart();
                    controllerContext.HttpContext.Session[sessionKey] = cart;
                }
                return cart;
            }
        }
    }

    在全局文件Global.asax中把自定义的ModelBinder注册到MVC中:

    protected void Application_Start()
            {
                ......
    
                ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory());
                ModelBinders.Binders.Add(typeof(Cart), new CartModelBinder());
            }

    有了如上铺垫,在Cart控制器的方法中参数中,可以有一个类型为Cart的参数,该参数值直接从自定义的CartModelBinder中获取:

    using System.Linq;
    using System.Web.Mvc;
    using MySportsStore.IBLL;
    using MySportsStore.Model;
    using MySportsStore.WebUI.Models;
    using Ninject;
    
    namespace MySportsStore.WebUI.Controllers
    {
        public class CartController : BaseController
        {
            [Inject]
            public IProductService ProductService { get; set; }
    
            public CartController()
            {
                this.AddDisposableObject(ProductService);
            }
    
            public ActionResult Index(Cart cart, string returnUrl)
            {
                return View(new CartIndexViewModel
                {
                    //Cart = GetCart(),
                    Cart = cart,
                    ReturnUrl = returnUrl
                });
            }
    
            //添加到购物车
            public RedirectToRouteResult AddToCart(Cart cart, int Id, string returnUrl)
            {
                Product product = ProductService.LoadEntities(p => p.Id == Id).FirstOrDefault();
                if (product != null)
                {
                    //GetCart().AddItem(product, 1);
                    cart.AddItem(product, 1);
                }
                return RedirectToAction("Index", new {returnUrl});
            }
    
            //从购物车移除
            public RedirectToRouteResult RemoveFromCart(Cart cart, int Id, string returnUrl)
            {
                Product product = ProductService.LoadEntities(p => p.Id == Id).FirstOrDefault();
                if (product != null)
                {
                    //GetCart().RemoveLine(product);
                    cart.RemoveLine(product);
                }
                return RedirectToAction("Index", new {returnUrl});
            }
    
            public ViewResult Summary(Cart cart)
            {
                return View(cart);
            }
    
            private Cart GetCart()
            {
                Cart cart = (Cart)Session["Cart"];
                if (cart == null)
                {
                    cart = new Cart();
                    Session["Cart"] = cart;
                }
                return cart;
            }
    
        }
    }

    运行:

    15

      显示购物车内容 添加"移除"按钮

    在显示购物车内容视图页,除了要把购物车内容呈现,在其对应的视图模型中还必须有一个属性,用来存放先前的URL,然后点击页面的"继续购物"按钮,方才可以回到先前的界面。


    显示购物车内容视图页的View Model为:

    namespace MySportsStore.WebUI.Models
    {
        public class CartIndexViewModel
        {
            public Cart Cart { get; set; }
            public string ReturnUrl { get; set; }
        }
    }

    Cart/Index.cshtml视图:

    @model MySportsStore.WebUI.Models.CartIndexViewModel
    
    @{
        ViewBag.Title = "Index";
        Layout = "~/Views/Shared/_Layout.cshtml";
    }
    
    <table width="50%" align="left">
        <thead>
            <tr>
                <th align="left">产品名称</th>
                <th align="center">数量</th>          
                <th align="right">单价</th>
                <th align="right">小计</th>
                <th></th>
            </tr>
        </thead>
        <tbody>
            @foreach (var line in Model.Cart.Lines)
            {
                <tr>
                    <td align="left">@line.Product.Name</td>
                    <td align="center">@line.Quantity</td>
                    <td align="right">@line.Product.Price.ToString("c")</td>
                    <td align="right">@((line.Quantity * line.Product.Price).ToString("c"))</td>
                    <td>
                        @using (Html.BeginForm("RemoveFromCart", "Cart"))
                        {
                            @Html.Hidden("Id", line.Product.Id)
                            @Html.HiddenFor(x => x.ReturnUrl)
                            <input class="actionButtons" type="submit" value="移除"/>
                        }
                    </td>
                </tr>
            }
        </tbody>
        <tfoot>
            <tr>
                <td colspan="3" align="right">总计:</td>
                <td align="right">@Model.Cart.ComputeTotalValue().ToString("c")</td>
            </tr>
        </tfoot>
    </table>
    <p align="left" class="actionButtons" style=" 100%;clear: both">
        <a href="@Model.ReturnUrl">继续购物</a>
    </p>

    运行:

    16

      8.4 显示购物车摘要

    购物车摘要通常放在页面的公共部分,用来显示买了多少件商品,花了多少钱,并提供一个指向购物车显示页的链接,以部分视图的形式存在:

    @model MySportsStore.WebUI.Models.Cart
    
    @{
        Layout = null;
    }
    
    <div id="cart">
        <span class="caption">
            <b>购物车明细:</b>
            @Model.Lines.Sum(x => x.Quantity) 件,
            @Model.ComputeTotalValue().ToString("c")
        </span>
        
        @Html.ActionLink("结算", "Index", "Cart", new {returnUrl = Request.Url.PathAndQuery}, null)
    </div>

    然后把这块部分视图放到页面的公共部分,即Views/Shared/_Layout.cshtml中:

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width" />
        <title>@ViewBag.Title</title>
        <link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
    </head>
        <body>
            <div id="header" style="height: 200px;">
                 @{Html.RenderAction("Summary", "Cart");}
                <div class="title">体育用品商店</div>
            </div>
            <div id="categories">
                @{Html.RenderAction("Menu","Nav");}
            </div>
            <div id="content">
                @RenderBody()
            </div>
           
            @Scripts.Render("~/bundles/jquery")
            @RenderSection("scripts", required: false)
        </body>
    </html>

    运行:

    17

    至此,购物车功能结束。

    源码在这里

    “MVC项目实践,在三层架构下实现SportsStore”系列包括:

    MVC项目实践,在三层架构下实现SportsStore,从类图看三层架构

    MVC项目实践,在三层架构下实现SportsStore-01,EF Code First建模、DAL层等

    MVC项目实践,在三层架构下实现SportsStore-02,DbSession层、BLL层

    MVC项目实践,在三层架构下实现SportsStore-03,Ninject控制器工厂等

    MVC项目实践,在三层架构下实现SportsStore-04,实现分页

    MVC项目实践,在三层架构下实现SportsStore-05,实现导航

    MVC项目实践,在三层架构下实现SportsStore-06,实现购物车

    MVC项目实践,在三层架构下实现SportsStore-07,实现订单提交

    MVC项目实践,在三层架构下实现SportsStore-08,部署到IIS服务器

    MVC项目实践,在三层架构下实现SportsStore-09,ASP.NET MVC调用ASP.NET Web API的查询服务

    MVC项目实践,在三层架构下实现SportsStore-10,连接字符串的加密和解密

    MVC项目实践,在三层架构下实现SportsStore-11,使用Knockout实现增删改查

  • 相关阅读:
    Discuz论坛自动发帖机
    C#测试数据库连接是否成功
    JS重写提示框(confirm)
    随笔 选择
    随笔 诚实
    web项目经理手册【1】版本控制流程
    Asp.net多层架构中的变量引用与传递
    ASP.NET跨页面传值技巧总结
    web项目经理手册【3】Code Review
    web项目经理手册【7】项目经理需要铭记在心的话
  • 原文地址:https://www.cnblogs.com/darrenji/p/3813109.html
Copyright © 2020-2023  润新知