第9章 SportsStorePeta 完成购物车

一、使用模型绑定

    模型绑定:通过HTTP请求来创建一些C#对象,目的把它们做为参数值传递给动作方法。

  创建自定义模型绑定器 (通过实现IModelBinder接口。)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using SportsStorePeta.Domain.Entities;

namespace SportsStorePeta.WebUI.Binders
{
    public class CartModelBinder  :IModelBinder
    {
        private const string seesionKey = "Cart";
        public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            //通过会话获取Cart
            Cart cart = (Cart) controllerContext.HttpContext.Session[seesionKey];
            //若会话中没有Cart,则创建
            if (cart == null)
            {
                cart=new Cart();
                controllerContext.HttpContext.Session[seesionKey] = cart;
            }
            //返回cart
            return cart;
        }
    }
}

  注册CartModelBinder类:

    //注册自定义模型绑定器
    ModelBinders.Binders.Add(typeof(Cart),new CartModelBinder());

  更新CartController:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using SportsStorePeta.Domain.Abstract;
using SportsStorePeta.Domain.Entities;
using SportsStorePeta.WebUI.Models;

namespace SportsStorePeta.WebUI.Controllers
{
    public class CartController : Controller
    {
        private IProductRepository _repository;

        public CartController(IProductRepository repo)
        {
            _repository = repo;
        }

        public ViewResult Index(Cart cart,string returnUrl)
        {
            return View(new CartIndexViewModel { Cart = cart, ReturnUrl = returnUrl });
        }

        public RedirectToRouteResult AddToCart(Cart cart, int productId, string returnUrl)
        {
            Product product = _repository.Products.FirstOrDefault(p => p.ProductId == productId);
            if (product != null)
            {
                cart.AddItem(product, 1);
            }
            return RedirectToAction("Index", new {returnUrl});
        }

        public RedirectToRouteResult RemoveFromCart(Cart cart, int productId, string returnUrl)
        {
              Product product = _repository.Products.FirstOrDefault(p => p.ProductId == productId);
            if (product != null)
            {
                cart.RemoveLine(product);
            }
            return RedirectToAction("Index", new { returnUrl });
        }

    }
}

二、完成购物车

  1.删除购物车物品

    引入删除按钮

    <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("ProductId",line.Product.ProductId)
                    @Html.HiddenFor(x=>x.ReturnUrl)
                    <input class="actionButtons" type="submit" value="删除"/>
                }</td>
        </tr>
    }
    </tbody>

  2.添加购物车摘要

    添加Summary方法 

       public PartialViewResult Summary(Cart cart)
        {
            return PartialView(cart);
        }

    添加Summary分部视图

@model SportsStorePeta.Domain.Entities.Cart

<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>

    将购物车摘要分部视图添加到布局

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width" />
    <title>@ViewBag.Title</title>
    <link href="/Content/Site.css" type="text/css" rel="stylesheet"/>
</head>
<body>
<div id="header">
    @{Html.RenderAction("Summary","Cart");}
    <div class="title">体育用品</div>
</div>
<div id="categories">
   @{
       Html.RenderAction("Menu","Nav");
   }
</div>
<div id="content">
    @RenderBody()
</div>
</body>
</html>

  添加CSS

div#cart {
    float: right;margin: .8em;color: silver;
    background-color: #555;padding: .5em  .5em  .5em  1em;
}
div#cart a {
    text-decoration: none;padding: .4em 1em .4em 1em;line-height: 2.1em;
    margin-left: .5em;background-color: #333;color: white;border: 1px solid black;
}

三、递交订单

  1.扩充域模型ShippingDetails类(添加引用System.ComponentModel.DataAnnotations)    

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.DataAnnotations;

namespace SportsStorePeta.Domain.Entities
{
   public class ShippingDetails
    {
       [Required(ErrorMessage = "请输入姓名")]
       public string Name { get; set; }

       [Required(ErrorMessage = "请输入地址")]
       public string Line1 { get; set; }
       public string Line2 { get; set; }
       public string Line3 { get; set; }

       [Required(ErrorMessage = "请输入城市名称")]
       public string City { get; set; }

       [Required(ErrorMessage = "请输入省份名称")]
       public string State { get; set; }

       public string Zip { get; set; }

       [Required(ErrorMessage = "请输入国家名称")]
       public string Country { get; set; }

       public bool GiftWrap { get; set; }
    }
}

  2.添加结算过程

    添加结算按钮 

<p align="center" class="actionButtons">
    <a href="@Model.ReturnUrl">继续购物</a>
    @Html.ActionLink("结算","Checkout")
</p>

    Checkout动作方法

        public ViewResult Checkout()
        {
            return View(new ShippingDetails());
        }

    Checkout视图

@model SportsStorePeta.Domain.Entities.ShippingDetails

@{
    ViewBag.Title = "结算";
}

<h2>现在结算</h2>
请填写联系单:
@using (Html.BeginForm())
{
    <h3>收货信息:</h3>
    <div>收货人:@Html.EditorFor(x => x.Name)</div>
    <h3>地址:</h3>
    <div>详细地址1:@Html.EditorFor(x => x.Line1)</div>
    <div>详细地址2:@Html.EditorFor(x => x.Line2)</div>
    <div>详细地址3:@Html.EditorFor(x => x.Line3)</div>
    <div>城市:@Html.EditorFor(x => x.City)</div>
    <div>省:@Html.EditorFor(x => x.State)</div>
    <div>邮编:@Html.EditorFor(x => x.Zip)</div>
    <div>国家:@Html.EditorFor(x => x.Country)</div>
    <label>@Html.EditorFor(x => x.GiftWrap)礼盒包装</label>
    <p align="center">
        <input class="actionButtons" type="submit" value="完成订单"/>
    </p>

} 

 3.实现订单处理器

   定义接口

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SportsStorePeta.Domain.Entities;

namespace SportsStorePeta.Domain.Abstract
{
  public  interface IOrderProcessor
  {
      void ProcessOrder(Cart cart, ShippingDetails shippingDetails);
  }
}

   实现接口:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Mail;
using System.Net;
using SportsStorePeta.Domain.Abstract;
using SportsStorePeta.Domain.Entities;

namespace SportsStorePeta.Domain.Concrete
{
    public class EmailSettings
    {
        public string MailToAddress = "[email protected]";
        public string MailFromAddress = "[email protected]";
        public bool UseSsl = true;
        public string Username = "123456779";
        public string Password = "123123123123";
        public string ServerName = "smtp.163.com";
        public int ServerPort = 25;
        public bool WriteAsFile = false;
        public string FileLocation = @"c:\sports_store_emails";
    }

   public  class EmailOrderProcessor  :IOrderProcessor
   {
       private EmailSettings _emailSettings;

       public EmailOrderProcessor(EmailSettings settings)
       {
           _emailSettings = settings;
       }

       public void ProcessOrder(Cart cart, ShippingDetails shippingDetails)
       {
           using (var smtpClient = new SmtpClient())
           {
               smtpClient.EnableSsl = _emailSettings.UseSsl;
               smtpClient.Host = _emailSettings.ServerName;
               smtpClient.Port = _emailSettings.ServerPort;
               smtpClient.UseDefaultCredentials = false;
               smtpClient.Credentials = new NetworkCredential(_emailSettings.Username, _emailSettings.Password);

               if (_emailSettings.WriteAsFile)
               {
                   smtpClient.DeliveryMethod = SmtpDeliveryMethod.SpecifiedPickupDirectory;
                   smtpClient.PickupDirectoryLocation = _emailSettings.FileLocation;
                   smtpClient.EnableSsl = false;
               }

               StringBuilder body = new StringBuilder().Append("收到新订单").Append("----").Append("物品为:");

               foreach (var line in cart.Lines)
               {
                   var subtotal = line.Product.Price * line.Quantity;
                   body.AppendFormat("{0}X{1}(金额:{2:c}", line.Quantity, line.Product.Name, subtotal);
               }

               body.AppendFormat("总价:{0:c}", cart.ComputeTotalValue())
                   .AppendLine("---")
                   .AppendLine("邮寄给:")
                   .AppendLine(shippingDetails.Name)
                   .AppendLine(shippingDetails.Line1)
                   .AppendLine(shippingDetails.Line2 ?? "")
                   .AppendLine(shippingDetails.Line3 ?? "")
                   .AppendLine(shippingDetails.City)
                   .AppendLine(shippingDetails.State ?? "")
                   .AppendLine(shippingDetails.Country)
                   .AppendLine(shippingDetails.Zip)
                   .AppendLine("---")
                   .AppendFormat("{0}礼品包装:", shippingDetails.GiftWrap ? "需要" : "不要");

               MailMessage mailMessage = new MailMessage(_emailSettings.MailFromAddress, _emailSettings.MailToAddress,
                   "新的订单", body.ToString());
               if (_emailSettings.WriteAsFile)
               {
                   mailMessage.BodyEncoding = Encoding.UTF8;
               }
               smtpClient.Send(mailMessage);

           }
       }
    }
}

  4.注册实现

private void AddBindings()
        {
            //Ninject绑定
            //1.添加模拟IproductRepository实现
            //Mock<IProductRepository> mock=new Mock<IProductRepository>();
            //mock.Setup(m => m.Products).Returns(
            //    new List<Product>
            //    {
            //        new Product {Name = "Football", Price = 35},
            //        new Product {Name = "Surf board", Price = 179},
            //        new Product {Name = "Running shoes", Price = 87}
            //    }.AsQueryable());
            //永久绑定
           // _ninjectKernel.Bind<IProductRepository>().ToConstant(mock.Object);
            _ninjectKernel.Bind<IProductRepository>().To<PpProductRepository>();

            //添加支付绑定
            EmailSettings emailSettings = new EmailSettings()
            {
                WriteAsFile = Boolean.Parse(ConfigurationManager.AppSettings["Email.writeAsFile"] ?? "false")
            };
            _ninjectKernel.Bind<IOrderProcessor>().To<EmailOrderProcessor>().WithConstructorArgument("settings",emailSettings );
        }
<appSettings>
    <add key="webpages:Version" value="2.0.0.0" />
    <add key="webpages:Enabled" value="false" />
    <add key="PreserveLoginUrl" value="true" />
    <add key="ClientValidationEnabled" value="true" />
    <add key="UnobtrusiveJavaScriptEnabled" value="true" />
    <add key="Email.writeAsFile" value="true"/>
  </appSettings>

  5.完成购物车控制器

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using SportsStorePeta.Domain.Abstract;
using SportsStorePeta.Domain.Entities;
using SportsStorePeta.WebUI.Models;

namespace SportsStorePeta.WebUI.Controllers
{
    public class CartController : Controller
    {
        private IProductRepository _repository;
        private IOrderProcessor _orderProcessor;

        public CartController(IProductRepository repo,IOrderProcessor orderProcessor)
        {
            _repository = repo;
            _orderProcessor = orderProcessor;

        }

        public ViewResult Index(Cart cart,string returnUrl)
        {
            return View(new CartIndexViewModel { Cart = cart, ReturnUrl = returnUrl });
        }

        public RedirectToRouteResult AddToCart(Cart cart, int productId, string returnUrl)
        {
            Product product = _repository.Products.FirstOrDefault(p => p.ProductId == productId);
            if (product != null)
            {
                cart.AddItem(product, 1);
            }
            return RedirectToAction("Index", new {returnUrl});
        }

        public RedirectToRouteResult RemoveFromCart(Cart cart, int productId, string returnUrl)
        {
              Product product = _repository.Products.FirstOrDefault(p => p.ProductId == productId);
            if (product != null)
            {
                cart.RemoveLine(product);
            }
            return RedirectToAction("Index", new { returnUrl });
        }

        public PartialViewResult Summary(Cart cart)
        {
            return PartialView(cart);
        }

        public ViewResult Checkout()
        {
            return View(new ShippingDetails());
        }
        [HttpPost]
        public ViewResult Checkout(Cart cart, ShippingDetails shippingDetails)
        {
            if (!cart.Lines.Any())
            {
              ModelState.AddModelError("","您的购物车是空的!");
            }
            if (ModelState.IsValid)
            {
                _orderProcessor.ProcessOrder(cart,shippingDetails);
                cart.Clear();
                return View("Completed");
            }
            else
            {
                return View(shippingDetails);
            }
        }  

    }
}

  6.显示验证错误

@model SportsStorePeta.Domain.Entities.ShippingDetails

@{
    ViewBag.Title = "结算";
}

<h2>现在结算</h2>
请填写联系单:
@using (Html.BeginForm())
{
    @Html.ValidationSummary()
    <h3>收货信息:</h3>
    <div>收货人:@Html.EditorFor(x => x.Name)</div>
    <h3>地址:</h3>
    <div>详细地址1:@Html.EditorFor(x => x.Line1)</div>
    <div>详细地址2:@Html.EditorFor(x => x.Line2)</div>
    <div>详细地址3:@Html.EditorFor(x => x.Line3)</div>
    <div>城市:@Html.EditorFor(x => x.City)</div>
    <div>省:@Html.EditorFor(x => x.State)</div>
    <div>邮编:@Html.EditorFor(x => x.Zip)</div>
    <div>国家:@Html.EditorFor(x => x.Country)</div>
    <label>@Html.EditorFor(x => x.GiftWrap)礼盒包装</label>
    <p align="center">
        <input class="actionButtons" type="submit" value="完成订单"/>
    </p>

}

  7.显示致谢页面Completed.cshtml视图 

@{
    ViewBag.Title = "订单已提交";
}

<h2>谢谢您的惠顾,祝您生活愉快 ^_^</h2>
我们将第一时间将物品送到您的手中。

源码:http://yunpan.cn/cds74dVnXuteQ访问密码 7dbb

 

时间: 2024-08-05 11:17:51

第9章 SportsStorePeta 完成购物车的相关文章

第11章 SportsStorePeta 安全性与收尾工作

一.实现管理控制的安全 修改web.config中的条目: <authentication mode="Forms"> <forms loginUrl="~/Account/Login" timeout="2880" > <credentials passwordFormat="Clear"> <user name="admin" password="12

第8章 SportsStorePeta 导航

一.添加导航控制 1.过滤产品列表 增强ProductsListViewModel public class ProductsListViewModel { public IEnumerable<ProductViewModel> Products { get; set; } public PageInfo PagingInfo { get; set; } public string CurrentCategory { get; set; } } 修改List动作方法 public ViewR

第7章 SportsStorePeta 一个真实的应用程序

SportsStorePeta应用程序开发 步骤: 1.创建解决方案和项目(Domain.WebUI.UnitTests) 2.添加引用 3.设置ID容器 4.设计域模型 5.创建抽象存储库 6.创建模仿存储库 7.添加控制器.动作及视图 8.设置路由 9.准备数据库及数据表 10.创建实例框架上下文 11.创建产品存储库 12.添加分页 13.改进路由及URL 14.设置内容样式CSS 15.创建分部视图 一.解决方案:SportsStorePeta,该应用程序将不使用EF,而使用PetaPo

第10章 SportsStorePeta 管理

一.添加分类管理 1.创建CRUD控制器 using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using SportsStorePeta.Domain.Abstract; namespace SportsStorePeta.WebUI.Controllers { public class AdminController : Contro

结合MVC.NET相关知识实现在线卖酒销售项目(四)

p { font-size: 16px; font-family: "微软雅黑" } 结合项目三内容 本章内容实现"购物车(增删改)"功能 一.在"BrasServer"文件添加"Web窗体"名为"BarShow.aspx",并编辑前台代码(把项目一BraClothList.aspx)前台代码模板复制进          来

全网首发 商业级支付宝小程序入门与实战

第1章 课程导学与准备工作 本章主要介绍为何学习支付宝小程序,以及开发支付宝小程序能为我们带来哪些收获.之后会为大家介绍本课程内容具体安排,最后给出如何学好这门课程的一些学习建议.希望大家都能通过这门课程,学有所成,学有所归. 1-1 课程导学 第2章 初识支付宝小程序 本章首先会为大家介绍如何配置开发环境,如何使用蚂蚁开发者工具,随后我们会一起开发第一个hello world小例子,通过对开发工具以及基本文件类型的介绍,让大家快速熟悉支付宝小程序.为后面技术的学习,项目的开发做好基本准备.大家

RocketMQ核心技术精讲与高并发抗压实战

第1章 课程介绍为什么掌握RocketMQ消息中间件技术对于跳槽,晋级如此重要?学习RocketMQ技术,为什么首选这门课程?电商平台双11高并发场景下是如何抗压的?MQ部分的落地是如何做的?这章讲重点为你解答这些疑惑 1-1 课前必读(不看会错过一个亿) 1-2 课程导学 试看第2章 RocketMQ初探门径本章主要带着小伙伴一起了解Apache RocketMQ 顶级开源消息中间件的整体介绍.概念模型与源码包编译安装部署,控制台使用.让小伙伴们对RocketMQ有一个初步的认识! 2-1 本

RocketMQ核心技术精讲与高并发抗压实战(最新完整)

课程目录:第1章 课程介绍为什么掌握RocketMQ消息中间件技术对于跳槽,晋级如此重要?学习RocketMQ技术,为什么首选这门课程?电商平台双11高并发场景下是如何抗压的?MQ部分的落地是如何做的?这章讲重点为你解答这些疑惑 1-1 课前必读(不看会错过一个亿)1-2 课程导学 试看第2章 RocketMQ初探门径本章主要带着小伙伴一起了解Apache RocketMQ 顶级开源消息中间件的整体介绍.概念模型与源码包编译安装部署,控制台使用.让小伙伴们对RocketMQ有一个初步的认识! 2

商业级支付宝小程序入门与实战

第1章 课程导学与准备工作[毕设优质项目][毕设优质项目]本章主要介绍为何学习支付宝小程序,以及开发支付宝小程序能为我们带来哪些收获.之后会为大家介绍本课程内容具体安排,最后给出如何学好这门课程的一些学习建议.希望大家都能通过这门课程,学有所成,学有所归.... 第2章 初识支付宝小程序本章首先会为大家介绍如何配置开发环境,如何使用蚂蚁开发者工具,随后我们会一起开发第一个hello world小例子,通过对开发工具以及基本文件类型的介绍,让大家快速熟悉支付宝小程序.为后面技术的学习,项目的开发做