petshop4.0 详解之七(PetShop表示层设计)

本文详细介绍了ASP.NET 2.0中的新特性,重点解析了Profile和Membership的功能实现,包括个性化用户信息存储及用户认证管理,并探讨了在PetShop 4.0系统中的具体应用。

6.4  ASP.NET 2.0新特性
由于PetShop 4.0是基于.NET Framework 2.0平台开发的电子商务系统,因而它在表示层也引入了许多ASP.NET 2.0的新特性,例如MemberShip、Profile、Master Page、登录控件等特性。接下来,我将结合PetShop 4.0的设计分别介绍它们的实现。

6.4.1  Profile特性

Profile提供的功能是针对用户的个性化服务。在ASP.NET 1.x版本时,我们可以利用Session、Cookie等方法来存储用户的状态信息。然而Session对象是具有生存期的,一旦生存期结束,该对象保留的值就会失效。Cookie将用户信息保存在客户端,它具有一定的安全隐患,一些重要的信息不能存储在Cookie中。一旦客户端禁止使用Cookie,则该功能就将失去应用的作用。

Profile的出现解决了如上的烦恼,它可以将用户的个人化信息保存在指定的数据库中。ASP.NET 2.0的Profile功能默认支持Access数据库和SQL Server数据库,如果需要支持其他数据库,可以编写相关的ProfileProvider类。Profile对象是强类型的,我们可以为用户信息建立属性,以PetShop 4.0为例,它建立了ShoppingCart、WishList和AccountInfo属性。

由于Profile功能需要访问数据库,因而在数据访问层(DAL)定义了和Product等数据表相似的模块结构。首先定义了一个IProfileDAL接口模块,包含了接口IPetShopProfileProvider:

  

因为PetShop 4.0版本分别支持SQL Server和Oracle数据库,因而它分别定义了两个不同的PetShopProfileProvider类,实现IPetShopProfileProvider接口,并放在两个不同的模块SQLProfileDAL和OracleProfileDAL中。具体的实现请参见PetShop 4.0的源代码。
同样的,PetShop 4.0为Profile引入了工厂模式,定义了模块ProfileDALFActory,工厂类DataAccess的定义如下:

在业务逻辑层(BLL)中,单独定义了模块Profile,它添加了对BLL、IProfileDAL和ProfileDALFactory模块的程序集。在该模块中,定义了密封类PetShopProfileProvider,它继承自System.Web.Profile.ProfileProvider类,该类作为Profile的Provider基类,用于在自定义配置文件中实现相关的配置文件服务。在PetShopProfileProvider类中,重写了父类ProfileProvider中的一些方法,例如Initialize()、GetPropertyValues()、SetPropertyValues()、DeleteProfiles()等方法。此外,还为ShoppingCart、WishList、AccountInfo属性提供了Get和Set方法。至于Provider的具体实现,则调用工厂类DataAccess创建的具体类型对象,如下所示:
private static readonly IPetShopProfileProvider dal = DataAccess.CreatePetShopProfileProvider();

定义了PetShop.Profile.PetShopProfileProvider类后,才可以在web.config配置文件中配置如下的配置节:

在配置文件中,针对ShoppingCart、WishList和AccountInfo(它们的类型分别为PetShop.BLL.Cart、PetShop.BLL.Cart、PetShop.Model.AddressInfo)属性分别定义了ShoppingCartProvider、WishListProvider、AccountInfoProvider,它们的类型均为PetShop.Profile.PetShopProfileProvider类型。至于Profile的信息究竟是存储在何种类型的数据库中,则由以下的配置节决定:
<add key="ProfileDAL" value="PetShop.SQLProfileDAL"/>

而键值为ProfileDAL的值,正是Profile的工厂类PetShop.ProfileDALFactory.DataAccess在利用反射技术创建IPetShopProfileProvider类型对象时获取的。

在表示层中,可以利用页面的Profile属性访问用户的个性化属性,例如在ShoppingCart页面的codebehind代码ShoppingCart.aspx.cs中,调用Profile的ShoppingCart属性:

在上述的代码中,Profile属性的值从何而来?实际上,在我们为web.config配置文件中对Profile进行配置后,启动Web应用程序,ASP.NET会根据该配置文件中的相关配置创建一个ProfileCommon类的实例。该类继承自System.Web.Profile.ProfileBase类。然后调用从父类继承来的GetPropertyValue和SetPropertyValue方法,检索和设置配置文件的属性值。然后,ASP.NET将创建好的ProfileCommon实例设置为页面的Profile属性值。因而,我们可以通过智能感知获取Profile的ShoppingCart属性,同时也可以利用ProfileCommon继承自ProfileBase类的Save()方法,根据属性值更新Profile的数据源。

6.4.2  Membership特性

PetShop 4.0并没有利用Membership的高级功能,而是直接让Membership特性和ASP.NET 2.0新增的登录控件进行绑定。由于.NET Framework 2.0已经定义了针对SQL Server的SqlMembershipProvider,因此对于PetShop 4.0而言,实现Membership比之实现Profile要简单,仅仅需要为Oracle数据库定义MembershipProvider即可。在PetShop.Membership模块中,定义了OracleMembershipProvider类,它继承自System.Web.Security.MembershipProvider抽象类。

OracleMembershipProvider类的实现具有极高的参考价值,如果我们需要定义自己的MembershipProvider类,可以参考该类的实现。
事实上OracleMemberShip类的实现并不复杂,在该类中,主要是针对用户及用户安全而实现相关的行为。由于在父类MembershipProvider中,已经定义了相关操作的虚方法,因此我们需要作的是重写这些虚方法。由于与Membership有关的信息都是存储在数据库中,因而OracleMembershipProvider与SqlMembershipProvider类的主要区别还是在于对数据库的访问。对于SQL Server而言,我们利用aspnet_regsql工具为Membership建立了相关的数据表以及存储过程。也许是因为知识产权的原因,Microsoft并没有为Oracle数据库提供类似的工具,因而需要我们自己去创建membership的数据表。此外,由于没有创建Oracle数据库的存储过程,因而OracleMembershipProvider类中的实现是直接调用SQL语句。以CreateUser()方法为例,剔除那些繁杂的参数判断与安全性判断,SqlMembershipProvider类的实现如下:

代码中,aspnet_Membership_CreateUser为aspnet_regsql工具为membership创建的存储过程,它的功能就是创建一个用户。

OracleMembershipProvider类中对CreateUser()方法的定义如下:

一、Model主要功能: 1、 将每一个“业务实体”抽象成“(瘦数据)类”,可以很好地“划分”各个“对象”,操作更加清晰 2、 用于在应用程序各层之间传递数据,被用做传输数据的“容器” 3、 这就是所谓的“建模”过程! 4、 Model各个类(Model文件夹中的各个文件)划分或者说编制的原则,更趋向于模拟整个系统中的业务实体 二、实现细节: 1、 PetShop中Model的规划与数据库表的关系: (1) AccountInfo类——Account表 (2) AddressInfo类——无直接对应关系(对应Account表中一部分字段) (3) CartItemInfo类——无直接对应关系 (4) CreditCardInfo类——无直接对应关系 (5) ItemInfo类——Item表 (6) LineItemInfo类——无直接对应关系 (7) OrderInfo类——Orders表 (8) ProductInfo类——Product表 2、 为每一个Model中的类都标记了[Serializable],说明这些类可以被传行化,但是不能被继承! 3、 AccountInfo.cs文件:用户在网站注册的信息,及喜好选择情况 4、 使用构造函数可以初始化私有字段;使用属性可以读取私有字段(但使用属性不能设置私有字段的值) 5、 其中包含一个AddressInfo类的私有变量,和一个AddressInfo类的属性 6、 命名空间为PetShop.Model 7、 AddressInfo.cs文件:用户真实的个人姓名、住址和电话号码等信息 8、 与AccountInfo不同的是,AddressInfo类允许使用属性设置私有变量的值 9、 CartItemInfo.cs文件:描述购物车中每一种所选商品的信息的类 10、 该“类”对象的某些信息(如这里的Subtotal属性)可能并不是此类的“自然信息”,而需要经过简单计算而得到!这些简单但必要的信息也要在类的设计中体现出来! 11、 CreditCardInfo.cs文件:表示特定一张信用卡的信息 12、 ItemInfo.cs文件:一个Item指的是category"product"item,如猫"波斯猫"成年男波斯猫(或成年女波斯猫)。这个文件表示一个Item的所有信息 13、 productDesc字段的作用? 14、 LineItemInfo.cs文件:注意与CartItemInfo类的区别!LineItemInfo是用来描述用户最终确认的订单当中的某一种类的产品的信息的类 15、 同样包括了Subtotal属性 16、 OrderInfo.cs文件:用于显示用户某一个订单具体信息的类,在此一个订单当中,可能包括多个商品种类,即包括多个LineItemInfo对象(实际上在OrderInfo类中也确实存在LineItemInfo类型对象的一个数组!) 17、 ProductInfo.cs文件:包括一个特定Product的信息,如波斯猫 三、启发: 1、 来自Directory项目结束后的启发,以后做设计的时候,要将每个实体抽象为一个类,在整个系统中进行操作。 2、 在任意一个类当中,可能不只包括此实体类的自然信息,也可以包括一些对其他地方数据调用有用的属性信息,如根据数量和单价计算出来的总价属性,或者标志此实体的直属上级实体的属性 四、问题: 1、 忽然发现在MSDN上有文章,关于数据实体的:浏览 2、 抽象这些业务实体模型为瘦数据类的原则是什么?什么样的业务实体可以被抽象,或者说进行抽象后更有意义? 3、 ItemInfo类中,productDesc字段的作用? 一、IDAL主要功能: 1、 这完全是“工厂模式”的一部分实现而已 2、 这是一组接口类,其中包括了每个要公开的数据访问方法。为每个数据库产品单独编写的DAL(数据访问层)都要实现这组接口所定义的方法 3、 也就是规定了在DAL中编写的“对用户账号进行操作的类”所必须执行的方法! 4、 IDAL要达到的目的是:实现业务逻辑(BLL)与数据库访问(DAL)的完全分离!!! 5、 IDAL各个类(IDAL文件夹中的各个文件)划分或者说编制的原则,更趋向于“将对数据库的不同操作进行归类”,考虑的主要方面是数据库操作!!!例如,有对用户账号进行的一系列数据库操作,则将这一系列操作统一放置于IAccount接口(将来实现后的Account类)文件当中 二、实现细节: 1、 IAccount.cs文件:为针对不同数据库产品编写的“操作用户账号的类”所必须实现的一系列方法定义契约 2、 IInventory.cs文件:定义“操作库存量的类”所必须实现的一系列方法;或者说将操作库存量的一系列(所有)方法做一个汇总 3、 IItem.cs文件:定义“操作某一Item的类”所必须实现的一系列方法。(Item在Model中定义了,是指具体某一类别的Product,如男猫或女猫) 4、 对于其中的GetItemsByProduct()方法,返回的是一个ArrayList的接口类型(IList)的对象(见问题部分!) 5、 IOrder.cs文件:定义了一组DAL层中“操作用户订单的类”必须执行的方法。其中包括“添加一张新订单”的方法Insert()和根据一个已有的订单号取得此订单详细信息的方法GetOrder(),此方法返回的是一个OrderInfo对象。(Model中的OrderInfo类模型定义了用户的某一张Order中相关的信息,如发货地点,总价,信用卡号码等等) 6、 IProduct.cs文件:定义类一组在DAL层中编写的“对Product进行操作的类” 7、 IProfile.cs文件:定义一组在DAL层编写的“对用户Profile进行操作的类” 三、启发: 1、 这样就可以让在BLL层只针对IDAL层定义的接口进行编程(使用IDAL接口定义的这些方法)就可以了!!!无论在底层使用了什么厂家的数据库产品,有区别的只是针对此数据库产品编写的DAL层(相同的方法,如SignIn()方法,对于不同的数据库产品,可能有不同的实现方式!),而不会影响到上层的BLL层已经编写好的内容! 2、 从这里可以看到软件架构师和程序员工作上的区别!架构师要负责的是搭建系统的层次结构,定义接口;而程序员要负责的是针对接口的具体代码实现过程! 3、 这个IDAL接口的使用,主要是为了保证在底层数据库实现,甚至数据库产品发生变化的时候,不需要对上层BLL层的业务逻辑进行大量的修改!BLL层针对IDAL接口编程即可!!! 4、 IDAL文件夹中定义的所有的接口中的所有的方法,包括了整个程序要对数据库进行操作的所有方法 5、 由于PetShop只是一个演示程序,所以若对数据库操作的某一类别(如对Account进行操作)中定义的“操作类”不够用(如除了接口中定义的几个方法外还需要其他Account操作),还可以在接口中追加其他的方法,用以约束DAL层的实现类(如Account类)必须执行这些新增加的方法! 四、问题: 1、 定义这组接口后,如何保证为每个单独的数据库产品编写的DAL都执行这组接口所定义的方法?(答案:编写DAL的时候必须刻意保证,否则就失去了定义IDAL层分割BLL和DAL层的意义!!!) 2、 既然在实现GetItemsByProduct()方法的时候,也是要把返回的所有Item对象添加到一个ArrayList当中去,但为什么GetItemsByProduct()方法返回的不是一个ArrayList对象,而是一个IList接口的对象呢?仅仅是基类引用的方法吗?这里的基类引用有什么用呢?是否是由于为了不限制使用的数组形式?执行IList则可以使用ArrayList,也可以使用其他形式的数组??? 3、 IProduct.cs文件中为何要将查询条件参数定义为一个string类型的数组?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值