9.3.13 购物处理
在这个项目中,将允许用户在没有注册、登录的情况下将专辑加入购物车,但是,在完成结账的时候必须完成注册工作。购物和结账将会被分离到两个控制器中:一个Shopping-Cart控制器,允许匿名用户使用购物车,另一个Checkout控制器处理结账。下面先从购物车的控制器开始,然后处理结帐。
1.创建购物记录类、订单类和订单明细类
在购物车和结账的处理中将会使用到一些新的类,在Models文件夹上右击,然后使用下面的代码增加一个新的类Cart,这个类包括记录号、购物车号、商品编号、商品数量、日期、商品索引等。
图9-58 要求授权的登录
这个类非常类似前面使用的类,除了RecordId属性上的[Key]标注之外。购物车拥有一个字符串类型的名为CartId的标识,用来允许匿名用户使用购物车,但是,CartId并不是表的主键。因为允许一个购物车内容纳多个商品,所以购物车编号不能作为主键,所以单独定义一个recordID字段来作为记录的唯一标识。表的主键是整数类型的名为RecordId的字段,根据约定,EF CodeFirst将会认为表的主键名为CartId或者albumId,不过,如果需要的话,可以很容易地通过标注或者代码来重写这个规则。这里例子演示了在使用EF Code-First的时候。当表不是约定的样子时,也不必被约定所局限。因为用到[key],所以要添加引用using System.ComponentModel.DataAnnotations。
然后,使用下面的代码增加订单Order类,在这个类中有订单编号、下单日期、用户相关信息等。
在这个类定义中使用了验证功能,所以需要添加与验证相关的引用。这个类跟踪订单的汇总和发货信息。在这个类中定义了一个OrderDetails属性来查看订单的明细。
最后,定义OrderDetail类,以描述订单详情。因为orderDetail类的记录是系统生成的,不需要用户输入,所以不需要增加验证功能。代码如下:
把MusicStoreEntities更新一下,以便包含新定义的模型类,更新之后的MusicStoreEnti-ties代码如下:
2.创建购物车类
在Models文件夹中创建ShoppingCart类来处理购物车对Cart购物记录类的数据访问,另外,还需要处理在购物车中增加或者删除项目的业务逻辑。
因为并不希望用户必须登录系统才可以使用购物车,对于没有登录的用户,需要为他们创建一个临时的唯一标识,这里使用GUID,或者被称为全局唯一标识符,对于已经登录的用户,直接使用他们的名称,这个标识将保存在Session中。
注意:Session会话可以很方便地存储用户的信息,在用户离开站点之后,这些信息将会过期,滥用Session信息会对大型站点产生影响,这里使用Session达到演示目的。
ShoppingCart类提供了如下的方法:
AddToCart,将专辑作为参数加入到购物车中,在Cart表中跟踪每个专辑的数量,在这个方法中,将会检查是在表中增加一个新行,还是仅仅在用户已经选择的专辑上增加数量。
RemoveFromCart,通过专辑的标识从用户的购物车中将这个专辑的数量减少1,如果用户仅仅剩下一个,那么就删除这一行。
EmptyCart,删除用户购物车中所有的项目。
GetCartItems,获取购物项目的列表用来显示或者处理。
GetCount,获取用户购物车中专辑的数量
GetTotal,获取购物车中商品的总价
CreateOrder,将购物车转换为结账处理过程中的订单。
GetCart,这是一个静态方法,用来获取当前用户的购物车对象,它使用GetCartId方法来读取保存当前Session中的购物车标识,GetCartId方法需要HttpContextBase以便获取当前的Session。
实际的代码如下:
分析上面的类成员函数:
其中获取购物车方法GetCart重载了两个,一个使用HttpContextBase来获取购物车信息,一个使用Controller来调用上面使用HttpContextBase重载方法来获取购物车信息。这种做法可以通用将来使用HTTPContext和使用控制器的情况,非常方便。
AddToCart方法用于将来添加新项目到购物车中。该方法中做了判断,可以添加重复项目,每次可以多加一个。但没有可以添加指定数量的方式。如果将来在购物车中要添加指定数量时,这里要使用这个类的话,就比较麻烦了,就需要重复调用AddToCart方法,这样每次都要先查出该购买项,然后判断,再增1,再保存,依次循环。如果出现意外的话,那么就不好处理了。这里要提供将来在界面中可以添加指定数量的功能的话,就应该再重构一下该方法。
RemoveFromCart方法用于在购物车中移除项,同样的每次也只操作一项。如果要移除这个购物内容话,而当前数量为10的话,岂不是要调用10次这个方法,因此这里可以重构一个移除项方法。
GetCartId获取购物车编号,这里考虑了用户登录与没有登录的情况下处理。登录了就使用用户名作为购物车的标识符,匿名用户的话创建了一个临时的全局唯一标识符来作为购物车标识符。同时还提供了MigrateCart方法用来将匿名用户的购物车迁移到登录用户中去的方法。这个方法非常简单,就是将购物车的标识符修改为用户名作为标识符。
3.创建对应shoppingCart的视图模型
ShoppingCart控制器需要向视图传递复杂的信息,这些信息与现有的模型并不完全匹配,也不希望修改模型来适应视图的需要;模型类应该表示领域信息,而不是用户界面。一个解决方案是使用ViewBag来向视图传递信息,就像在Store Manager中的列表处理中那样,但是如果通过ViewBag来传递大量信息就不好管理了。
另外一个解决方案是使用视图模型模式,如果使用这个模式,就需要创建强类型的用于视图场景的类来表示信息,这个类拥有视图所需要的值或者内容。控制器填充信息,然后传递这种类的对象供视图使用,这样就可以得到强类型的、编译时检查支持,并且在视图模板中带有智能提示。
将会创建两个视图模型用于ShoppingCart控制器:ShoppingCartViewModel将会用于用户的购物车,而ShoppingCartRemoveViewModel会用于在购物车中删除内容时确认提示信息。
首先在项目中创建ViewModels文件夹来组织项目文件,在项目上点击鼠标的右键,然后选择添加→新文件夹,添加项目文件夹viewModels,如图9-59所示。
图9-59 创建viewModels文件夹
然后,在ViewModels文件夹中增加ShoppingCartViewModel类,它包括两个属性,一个CartItem的列表,另外一个属性是购物车中的总价。代码如下:
然后,增加ShoppingCartRemoveViewModel类,它包括5个属性。
4.创建ShoppingCart控制器
Shopping Cart控制器有3个主要的目的:增加项目到购物车,从购物车中删除项目,查看购物车中的项目。控制器使用到刚刚创建的3个类:ShoppingCartViewModel,ShoppingCar-tRemoveViewModel和ShoppingCart。在项目中使用空的控制器模板创建Shopping Cart控制器,如图9-60所示。像StoreController和StoreManagerController一样,在控制器中增加一个MusicStoreEntities字段来操作数据。
图9-60 添加ShoppingCartController控制器
下面是已经完成的控制器代码,Index和Add方法看起来非常熟悉。Remove和Cart-Summary这两个Action方法处理两种特定的场景将在后面讨论。
CartSummary Action用于呈现购物车的描述信息,该Action属于部分视图,ChildAction-Only特性标记用于表示当前Action作为子操作进行调用,呈现出一个页面的一部分,用于将来组合一个完整的视图。
5.使用jQuery进行Ajax更新
下面将创建ShoppingCart的Index Action视图,这个视图使用强类型的ShoppingCart-ViewModel,像以前的视图一样,使用List视图模板,如图9-61所示。
在这里,不使用Html.ActionLink从购物车中删除项目,将会使用JQuery来包装客户端使用RemoveLink的类所有超级链接元素的事件,不是提交表单,而是通过客户端的事件向RemoveFromCart控制器方法发出Ajax请求,然后RemoveFromCart返回JSON格式的结果,这个结果被发送到在AjaxOptions的OnSucess参数中创建的JavaScript函数,在这里是hndleUpdate,handleUpdate函数解析JSON格式的结果,然后通过jQuery执行下面的4个更新:
图9-61 添加ShoppingCart模型的index方法的视图
■从列表中删除专辑;
■更新头部的购物车中的数量;
■向用户显示更新信息;
■更新购物车中的总价。
因为在Index视图中处理了删除的场景,就不再需要为RemoveFromCart方法增加额外的视图。下面是视图的完整代码。
6.更新store控制器下的Details视图
为了测试一下,需要向购物车中增加一些项目,更新Store的Details视图包含添加到购物车按钮,在这里,还需要包含后来增加的专辑的一些额外信息、流派、艺术家、价格等等。更新后的details视图如下:
7.测试
现在,可以在商店中通过购物车来购买和删除一些项目了。运行程序,浏览Store控制器的Index,然后单击某个分类来查看专辑的列表。再单击某个专辑来显示专辑的详细内容,现在已经有了加入购物车的按钮,如图9-62所示。单击加入购物车之后,可以在购物车中看到。如图9-63所示。在购物车中,可以单击从购物车中删除的链接从而删除购物车中的商品。