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="width: 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实现增删改查

你可能感兴趣的:(store)