项目实战SportsStore——订单处理模块
前面的步骤如果顺利完成,你的网站运行之后应该能够正常显示下面三个页面:
1.产品列表
2.购物车内容页面
在某个商品后面点击“添加到购物车”则出现下图页面:
此页面上点击“继续购物”按钮则返回到产品列表页面(第一幅图所示)。
- 如果你的网站没有看到这个页面,你需要对照参考教材“8.3 提交订单”之前的内容进行修改。这里给大家提供一个参考代码,请点击下载
提交订单模块
1.添加订单相关数据表,记录订单信息
提交购物车中的订单,需要将用户购买的商品写到数据库里面保存起来,以后发货、用户查看订单等操作时,才知道哪个用户购买了哪些东西。所以,我们需要在数据库中建立“订单”数据表。
“订单”数据表中的每条记录应该需要记录:用户姓名、用户街道地址、用户所在城市、省份,额外我们还可以增加两个字段:是否需要给产品包装、是否需要送货上门(不用的话就让用户自己去提货)。其中用户地址这里,考虑到每个用户可能会有多个地址,我们允许用户添加多个地址,所以可以多增加两个字段来存放不同的地址。
所以我们可以按如下步骤扩展SportsStore数据库,来添加一个“订单”Orders数据表:
(1)打开Sql Server Management Studio,登录数据库服务器。
(2)点击“新建查询”按钮,打开SQL语句编辑器,如图:
(3)输入SQL语句:
- USE [SportsStore]
- GO
- CREATE TABLE [dbo].[Orders](
- [OrderId] [int] IDENTITY(1,1) NOT NULL,
- [Name] [nvarchar](max) NULL,
- [Line1] [nvarchar](max) NULL,
- [Line2] [nvarchar](max) NULL,
- [Line3] [nvarchar](max) NULL,
- [City] [nvarchar](max) NULL,
- [State] [nvarchar](max) NULL,
- [GiftWrap] [bit] NULL,
- [Dispatched] [bit] NULL,
- CONSTRAINT [PK_Orders] PRIMARY KEY CLUSTERED ([OrderId] ASC))
(4)然后点击SSMS工具栏上的“执行”按钮(红色感叹号那个按钮),Orders数据表就创建好了。刷新一下数据库就可以在SportsStore里看到Orders表。
这里是使用SQL语句来创建数据库,当然你也可以使用图形化操作界面来添加一个数据表。
看到这里,你可能看到订单表里怎么没有有关产品的相关信息?一个订单有多个所购商品,上面“Orders”表包含了订单里除商品之外的相关信,对于订单里的所购商品,我们还需要再新增加一个数据表,这个表的每条记录应该包含如下信息:所购商品编号、所购订单编号、所购商品数量。不理解的同学,请自行脑补一下在学习数据库课程时,班级表和学生表之间的关系,这样就能理解订单表和所购商品表之间的关系了。
所购商品表的添加方法和订单表的添加方法是一样的,这里直接给出sql代码:
- USE [SportsStore]
- GO
- CREATE TABLE [dbo].[OrderLines](
- [OrderLinedId] [int] IDENTITY(1,1) NOT NULL,
- [Quntity] [int] NOT NULL,
- [Product_ProductID] [int] NULL,
- [Order_OrderId] [int] NULL,
- CONSTRAINT [PK_OrderLines] PRIMARY KEY CLUSTERED ([OrderLinedId] ASC),
- CONSTRAINT [FK_OrderLines_Orders] FOREIGN KEY([OrderLinedId]) REFERENCES [dbo].[Orders] ([OrderId]),
- CONSTRAINT [FK_OrderLines_Products] FOREIGN KEY([Product_ProductID]) REFERENCES [dbo].[Products] ([ProductID]))
2.添加数据模型
程序读取数据库里的数据记录,需要将其转换成对象,才能够在我们的程序中方便地使用。仿照Product类,我们对Orders表和OrderLines表分别添加数据模型。
在项目的Models文件夹下,添加Order.cs文件,然后按如下代码修改文件内容:
Order.cs文件
- using System;
- using System.Collections.Generic;
- namespace SportsStore.Models
- {
- public class Order
- {
- public int OrderId { get; set; }
- public string Name { get; set; }
- public string Line1 { get; set; }
- public string Line2 { get; set; }
- public string Line3 { get; set; }
- public string City { get; set; }
- public string State { get; set; }
- public bool GiftWrap { get; set; }
- public bool Dispatched { get; set; }
- public virtual List<OrderLine> OrderLines { get; set; }
- }
- public class OrderLine
- {
- public int OrderLineId { get; set; }
- public Order Order { get; set; }
- public Product Product { get; set; }
- public int Quantity { get; set; }
- }
- }
可以看到在Orders.cs文件中添加了两个类:Order和OrderLine。看到这里,你至少要明白两点:
1)一个.cs文件里可以包含1个、2个甚至更多个不同的类。
2)数据模型类的文件名要和数据库相应数据表的名字一致(除了单复数有区别之外)。如Order类对应的是Orders数据表,OrderLine类对应的是OrderLines数据表。使用EF读取数据库时,它自动会将数据记录映射成Order对象或OrderLine对象。如果非要另起名字,Entity Framework框架就找不到哪个数据模型类对应哪个数据表了,只有额外再增加其它代码告诉EF它才能搞清楚对应关系。目前我们还是按默认一致的方式来给模型类命名。
注意第22行,这里定义了一个List<OrderLine>类型的属性,代表订单里所包含的所有已购商品。这个属性叫做导航属性,使用了导航属性之后,对于某个Order对象,其OrderLines属性里的数据就是与该Order相关的所有已购商品集合。导航属性能够让我们非常方便地读取数据库中具有一对一、一对多关系的数据表数据。但必须有一点需要注意,OrderLines表中必须要定义对Order表的外键约束。我们在前面定义OrderLines数据表时是添加了约束的,读者可以回头去仔细看一下创建OrderLines表的SQL代码。至于前面那个virtual关键字,表示的是“延迟加载”或“惰性加载”。有关导航属性的更多解释,请参考:关系与导航属性
3.修改EF的上下文和存储库
数据表和模型类都有了,数据还是不能自己从数据库里跑到我们的程序里,还需要天假相关代码才可以实现数据库数据与我们程序之间的交互。
之前我们在实验中感受过使用ADO.NET的方式读写数据库,我们项目中不适用ADO.NET,而是使用EF组件。可以让我们编写最少的程序代码来解决复杂的问题。
使用EF,核心是上下文类。按如下步骤修改我们之前定义的EFDbContext类:
(1)打开Models->Repository文件夹下的EFDbContext.cs文件
(2)添加粗体字部分的那一行代码:
- using System.Data.Entity;
- namespace SportsStore.Models.Repository
- {
- public class EFDbContext:DbContext
- {
- public DbSet<Product> Products { get; set; }
- public DbSet<Order> Orders { get; set; }
- }
- }
我们在所有页面文件中不是直接使用这个EFDbContext类的,而是通过Repository类间接使用EFDbContext,所以还需要修改Repository类:
- using System.Collections.Generic;
- using System.Data.Entity;
- using System.Linq;
- namespace SportsStore.Models.Repository
- {
- public class Repository
- {
- private EFDbContext context = new EFDbContext();
- public IEnumerable<Product> Products
- {
- get {
- return context.Products;
- }
- }
- public IEnumerable<Order> Orders
- {
- get
- {
- return context.Orders.Include(o => o.OrderLines.Select(ol => ol.Product));
- }
- }
- public void SaveOrder(Order order)
- {
- if (order.OrderId == )
- {
- order = context.Orders.Add(order);
- foreach (OrderLine line in order.OrderLines)
- {
- context.Entry(line.Product).State
- = EntityState.Modified;
- }
- }
- else
- {
- Order dbOrder = context.Orders.Find(order.OrderId);
- if (dbOrder != null)
- {
- dbOrder.Name = order.Name;
- dbOrder.Line1 = order.Line1;
- dbOrder.Line2 = order.Line2;
- dbOrder.Line3 = order.Line3;
- dbOrder.City = order.City;
- dbOrder.State = order.State;
- dbOrder.GiftWrap = order.GiftWrap;
- dbOrder.Dispatched = order.Dispatched;
- }
- }
- context.SaveChanges();
- }
- }
- }
第21句:这里的Include方法是Linq技术中的一个方法,表示加载关联实体,通常用在关联查询中,有关Inclue关联查询的用法,可以参考这篇博文。在我们的程序里,Order类有一个关联实体OrderLines,context.Orders使用了Include之后,表示在查询Orders数据表记录时,要把每条记录中所关联的OrderLine记录(在OrderLines表中)也从数据库里读出来。对于Select方法,其作用将结果进行投影。本来每个OrderLine对象会包含四个属性(见OrderLine类定义),使用了Select之后,只剩下Product有数据,其余属性的数据不需要映射了。有关Select的用法,请参考这篇博文
4.添加结算页面
用户在购物车页面——CartView.aspx中点击“结算”按钮,跳转到结算页面Checkout.aspx。下面是Checkout.aspx页面的代码内容:
- <%@ Page Title="" Language="C#" MasterPageFile="~/Pages/Store.Master"
- AutoEventWireup="true" CodeBehind="Checkout.aspx.cs"
- Inherits="SportsStore.Pages.Checkout" %>
- <asp:Content ID="Content1" ContentPlaceHolderID="bodyContent" runat="server">
- <div id="content">
- <div id="checkoutForm" class="checkout" runat="server">
- <h2>Checkout Now</h2>
- 请输入你的详细信息,我们将尽快发货!
- <div id="errors" data-valmsg-summary="true">
- <ul><li style="display:none"></li></ul>
- <asp:ValidationSummary ID="ValidationSummary1" runat="server"/>
- </div>
- <h3>收货人姓名</h3>
- <div>
- <label for="name">姓名:</label>
- <input Property="Name" runat="server" />
- </div>
- <h3>地址</h3>
- <div>
- <label for="line1">地址 :</label>
- <input Property="Line1" runat="server" />
- </div>
- <div>
- <label for="line2">地址 :</label>
- <input Property="Line2" runat="server" />
- </div>
- <div>
- <label for="line3">地址 :</label>
- <input Property="Line3" runat="server" />
- </div>
- <div>
- <label for="city">城市:</label>
- <input Property="City" runat="server" />
- </div>
- <div>
- <label for="state">省:</label>
- <input Property="State" runat="server" />
- </div>
- <h3>可选</h3>
- <input type="checkbox" id="giftwrap" name="giftwrap" value="true"/>
- 是否包装所有商品?
- <p class="actionButtons">
- <button class="actionButtons" type="submit">提交订单</button>
- </p>
- </div>
- <div id="checkoutMessage" runat="server">
- <h2>谢谢!</h2>
- 感谢购买本店产品,我们已收到订单,将在最短时间内发货.
- </div>
- </div>
- </asp:Content>
操作处理代码存放在Checkout.aspx.cs文件中:
- using System;
- using System.Collections.Generic;
- using System.Web.ModelBinding;
- using SportsStore.Models;
- using SportsStore.Models.Repository;
- using SportsStore.Pages.Helpers;
- namespace SportsStore.Pages {
- public partial class Checkout : System.Web.UI.Page {
- protected void Page_Load(object sender, EventArgs e) {
- checkoutForm.Visible = true;
- checkoutMessage.Visible = false;
- if (IsPostBack) {
- Order myOrder = new Order();
- if (TryUpdateModel(myOrder,
- new FormValueProvider(ModelBindingExecutionContext))) {
- myOrder.OrderLines = new List<OrderLine>();
- Cart myCart = SessionHelper.GetCart(Session);
- foreach (CartLine line in myCart.Lines) {
- myOrder.OrderLines.Add(new OrderLine {
- Order = myOrder,
- Product = line.Product,
- Quantity = line.Quantity
- });
- }
- new Repository().SaveOrder(myOrder);
- myCart.Clear();
- checkoutForm.Visible = false;
- checkoutMessage.Visible = true;
- }
- }
- }
- }
- }
当用户在Checkout.aspx页面点击“提交订单”按钮之后,页面会提交(submit)到服务器,Checkout.aspx.cs文件被调入内存,并自动执行Page_Load方法。此时IsPostBack值是true(想一想为什么?),所以if(IsPostBack){...}语句块会执行。
当用户点击“提交订单”按钮时,表单Form里的所有表单元素都会一起送达服务器,如何取得用户输入的姓名、地址等数据呢?使用下面这条语句:
- new FormValueProvider(ModelBindingExecutionContext)
即可把表单Form里的表单元素全部获取。然后再调用TryUpdateModel方法,将表单元素里的所有值转换成对象,第一个参数是模型对象,表示你想把用书输入的表单数据最后转换成一个什么对象。
Checkout.aspx页面写好了,我们需要在购物车里添加一个“结算”按钮,当用户点击此按钮时,就跳转到Checkout.aspx页面执行相应的代码。添加“结算”按钮,只需要在CartView.aspx页面中添加下面一行代码:
- ...
- <p class="actionButtons">
- <a href="<%= ReturnUrl %>">Continue shopping</a>
- <a href="<%= CheckoutUrl %>">Checkout</a>
- </p>
- ...
粗体字部分就是要添加的内容。标签a的href属性需要调用CheckoutUrl属性才能获得其值。“CheckoutUrl”属性被定义在CartView.aspx.cs文件中:
- using System;
- using System.Collections.Generic;
- using SportsStore.Models;
- using SportsStore.Pages.Helpers;
- using SportsStore.Models.Repository;
- using System.Linq;
- using System.Web.Routing;
- namespace SportsStore.Pages {
- public partial class CartView : System.Web.UI.Page {
- protected void Page_Load(object sender, EventArgs e) {
- if (IsPostBack) {
- Repository repo = new Repository();
- int productId;
- if (int.TryParse(Request.Form["remove"], out productId)) {
- Product productToRemove = repo.Products
- .Where(p => p.ProductID == productId).FirstOrDefault();
- if (productToRemove != null) {
- SessionHelper.GetCart(Session).RemoveLine(productToRemove);
- }
- }
- }
- }
- public IEnumerable<CartLine> GetCartLines() {
- return SessionHelper.GetCart(Session).Lines;
- }
- public decimal CartTotal {
- get {
- return SessionHelper.GetCart(Session).ComputeTotalValue();
- }
- }
- public string ReturnUrl {
- get {
- return SessionHelper.Get<string>(Session, SessionKey.RETURN_URL);
- }
- }
- public string CheckoutUrl {
- get {
- return RouteTable.Routes.GetVirtualPath(null, "checkout",
- null).VirtualPath;
- }
- }
- }
- }
项目实战SportsStore——订单处理模块的更多相关文章
- angularJs项目实战!01:模块划分和目录组织
近日来我有幸主导了一个典型的web app开发.该项目从产品层次来说是个典型的CRUD应用,故而我毫不犹豫地采用了grunt + boilerplate + angularjs + bootstrap ...
- angularJs项目实战!02:前端的页面分解与组装
自从上一篇文章到现在已经有将近一个月的时间,我将精力放在了前端页面分解与组装,和angularjs如何与jquery.bootstrap.D3等一系列其他类库结合使用的经验总结上.由于公司新招了一些员 ...
- Java高级项目实战02:客户关系管理系统CRM系统模块分析与介绍
本文承接上一篇:Java高级项目实战之CRM系统01:CRM系统概念和分类.企业项目开发流程 先来CRM系统结构图: 每个模块作用介绍如下: 1.营销管理 营销机会管理:针对企业中客户的质询需求所建立 ...
- Github 上热门的 Spring Boot 项目实战推荐
最近经常被读者问到有没有 Spring Boot 实战项目可以学习,于是,我就去 Github 上找了 10 个我觉得还不错的实战项目.对于这些实战项目,有部分是比较适合 Spring Boot 刚入 ...
- 海量数据MySQL项目实战
主要内容包含 MySQL 典型数据库架构介绍.MySQL 主流数据库架构对比等理论性知识,然后从“订单.用户”两个项目实战,抛砖引玉,介绍亿级互联网业务数据库项目如何设计. MySQL 典型数据库架构 ...
- 手把手0基础项目实战(一)——教你搭建一套可自动化构建的微服务框架(SpringBoot+Dubbo+Docker+Jenkins)...
原文:手把手0基础项目实战(一)--教你搭建一套可自动化构建的微服务框架(SpringBoot+Dubbo+Docker+Jenkins)... 本文你将学到什么? 本文将以原理+实战的方式,首先对& ...
- Java高级项目实战03:CRM系统数据库设计
接上一篇:Java高级项目实战02:客户关系管理系统CRM系统模块分析与介绍 欢迎点击回顾,接下来我们说说 CRM系统数据库设计. 我们根据产品的原型搞以及UI组的设计稿, 接下来就要设计数据库, 一 ...
- 【腾讯Bugly干货分享】React Native项目实战总结
本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/577e16a7640ad7b4682c64a7 “8小时内拼工作,8小时外拼成长 ...
- webpack 教程 那些事儿04-webpack项目实战分析
这节主要讲解真正项目用用到的 webpack配置问题,项目实战篇 就像我们不会完全做一个项目,不用别人的轮子一样.这个配置我们借用 vue-cli 搭建的配置来研究,因为它已经足够优秀. 有了前面的基 ...
随机推荐
- Javascript 地图库收集
ArcGis leafletjs openlayers jvectormap
- Vue.js动态组件
动态组件: 1.定义: 几个组件放在同一个挂载点下,然后根据父组件的某个变量来决定显示哪个,或者都不显示. 2.动态切换原理: 在挂载点使用<component>标签,然后使用v-bind ...
- JavaScript实现选项卡(三种方法)
本文实例讲述了js选项卡的实现方法. 一.html代码: <div id="div1"> <input class="active" type ...
- 设计模式 笔记 状态模式 State
//---------------------------15/04/28---------------------------- //State 状态模式----对象行为型模式 /* 1:意图: ...
- Android开发者不可或缺的四大工具
Android开发者不可或缺的四大工具 android以其极强的开放性吸引着世界各地的开发者去开发各种各样的移动应用开发,而各种SDK更是为各个层次的开发者提供了一个可以尽情展示他们专业技能和创造性的 ...
- Spring学习(十九)----- Spring与WEB容器整合
首先可以肯定的是,加载顺序与它们在 web.xml 文件中的先后顺序无关.即不会因为 filter 写在 listener 的前面而会先加载 filter.最终得出的结论是:listener -> ...
- jinkens 构建java及vue 项目
- allegro 封装 (引脚编号修改)
1. 打开dra文件在find里面 off all 然后只点击text 2.点击需要更改的焊盘 3.菜单栏edit - text 4.弹出窗口修改即可
- Harbor 学习分享系列1 - centos7.4安装harbor1.5.2
centos7.4安装harbor1.5.2 前言 本系列分享将Harbor有关教程:分享形式会以百度云盘的形式进行分享,主要教程将以markdown格式进行分享:建议使用markdownpad2这款 ...
- 《Linux内核分析》第二周学习笔记
<Linux内核分析>第二周学习笔记 操作系统是如何工作的 郭垚 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/ ...