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

代码中,InsertUser()方法就是负责用户的创建,而在之前则需要判断创建的用户是否已经存在。InsertUser()方法的定义如下:

private   static   bool  InsertUser(OracleTransaction transaction,  int  userId,  string  email,  string  password,  int  passFormat,  string  passSalt,  string  passQuestion,  string  passAnswer,  bool  isApproved, DateTime dt)  {

 
string insert = "Insert INTO MEMBERSHIP (USERID, EMAIL, PASSWORD, PASSWORDFORMAT, PASSWORDSALT, PASSWORDQUESTION, PASSWORDANSWER, ISAPPROVED, CreateDDATE, LASTLOGINDATE, LASTPASSWORDCHANGEDDATE) VALUES (:UserID, :Email, :Pass, :PasswordFormat, :PasswordSalt, :PasswordQuestion, :PasswordAnswer, :IsApproved, :CDate, :LLDate, :LPCDate)";
 OracleParameter[] insertParms 
= new OracleParameter(":UserID", OracleType.Number, 10), new OracleParameter(":Email", OracleType.VarChar, 128), new OracleParameter(":Pass", OracleType.VarChar, 128), new OracleParameter(":PasswordFormat", OracleType.Number, 10), new OracleParameter(":PasswordSalt", OracleType.VarChar, 128), new OracleParameter(":PasswordQuestion", OracleType.VarChar, 256), new OracleParameter(":PasswordAnswer", OracleType.VarChar, 128), new OracleParameter(":IsApproved", OracleType.VarChar, 1), new OracleParameter(":CDate", OracleType.DateTime), new OracleParameter(":LLDate", OracleType.DateTime), new OracleParameter(":LPCDate", OracleType.DateTime) };
 insertParms[
0].Value = userId;
 insertParms[
1].Value = email;
 insertParms[
2].Value = password;
 insertParms[
3].Value = passFormat;
 insertParms[
4].Value = passSalt;
 insertParms[
5].Value = passQuestion;
 insertParms[
6].Value = passAnswer;
 insertParms[
7].Value = OracleHelper.OraBit(isApproved);
 insertParms[
8].Value = dt;
 insertParms[
9].Value = dt;
 insertParms[
10].Value = dt;

 
if(OracleHelper.ExecuteNonQuery(transaction, CommandType.Text, insert, insertParms) != 1)
  
return false;
 
else
  
return true;
}

在为Membership建立了Provider类后,还需要在配置文件中配置相关的配置节,例如SqlMembershipProvider的配置:

< membership  defaultProvider ="SQLMembershipProvider" >
 
< providers >
  
< add  name ="SQLMembershipProvider"  type ="System.Web.Security.SqlMembershipProvider"  connectionStringName ="SQLMembershipConnString"  applicationName =".NET Pet Shop 4.0"  enablePasswordRetrieval ="false"  enablePasswordReset ="true"  requiresQuestionAndAnswer ="false"  requiresUniqueEmail ="false"  passwordFormat ="Hashed" />
 
</ providers >
</ membership >

对于OracleMembershipProvider而言,配置大致相似:

< membership  defaultProvider ="OracleMembershipProvider" >
 
< providers >
  
< clear />
  
< add  name ="OracleMembershipProvider"  
   type
="PetShop.Membership.OracleMembershipProvider"  
   connectionStringName
="OraMembershipConnString"  
   enablePasswordRetrieval
="false"  
   enablePasswordReset
="false"  
   requiresUniqueEmail
="false"  
   requiresQuestionAndAnswer
="false"  
   minRequiredPasswordLength
="7"  
   minRequiredNonalphanumericCharacters
="1"  
   applicationName
=".NET Pet Shop 4.0"  
   hashAlgorithmType
="SHA1"  
   passwordFormat
="Hashed" />
 
</ providers >
</ membership >

有关配置节属性的意义,可以参考MSDN等相关文档。

6.4.3  ASP.NET登录控件

这里所谓的登录控件并不是指一个控件,而是ASP.NET 2.0新提供的一组用于解决用户登录的控件。登录控件与Membership进行集成,快速简便地实现用户登录的处理。ASP.NET登录控件包括Login控件、LoginView控件、LoginStatus控件、LoginName控件、PasswordRescovery控件、CreateUserWizard控件以及ChangePassword控件。
PetShop 4.0犹如一本展示登录控件用法的完美教程。我们可以从诸如SignIn、NewUser等页面中,看到ASP.NET登录控件的使用方法。例如在SignIn.aspx中,用到了Login控件。在该控件中,可以包含TextBox、Button等类型的控件,用法如下所示:

< asp:Login  ID ="Login"  runat ="server"  CreateUserUrl ="~/NewUser.aspx"  SkinID ="Login"  FailureText ="Login failed. Please try again." >
</ asp:Login >

又例如NewUser.aspx中对CreateUserWizard控件的使用:

< asp:CreateUserWizard  ID ="CreateUserWizard"  runat ="server"  CreateUserButtonText ="Sign Up"  InvalidPasswordErrorMessage ="Please enter a more secure password."  PasswordRegularExpressionErrorMessage ="Please enter a more secure password."  
RequireEmail
="False"  SkinID ="NewUser" >
< WizardSteps >
            
< asp:CreateUserWizardStep  ID ="CreateUserWizardStep1"  runat ="server" >
   
</ asp:CreateUserWizardStp >
 
</ WizardSteps >
</ asp:CreateUserWizard >

使用了登录控件后,我们毋需编写与用户登录相关的代码,登录控件已经为我们完成了相关的功能,这就大大地简化了这个系统的设计与实现。

6.4.4  Master Page特性

Master Page相当于是整个Web站点的统一模板,建立的Master Page文件扩展名为.master。它可以包含静态文本、html元素和服务器控件。Master Page由特殊的@Master指令识别,如:

< %@ Master  Language ="C#"  CodeFile ="MasterPage.master.cs"  Inherits ="MasterPage"  % >

使用Master Page可以为网站建立一个统一的样式,且能够利用它方便地创建一组控件和代码,然后将其应用于一组页。对于那些样式与功能相似的页而言,利用Master Page就可以集中处理为Master Page,一旦进行修改,就可以在一个位置上进行更新。

在PetShop 4.0中,建立了名为MasterPage.master的Master Page,它包含了header、LoginView控件、导航菜单以及用于呈现内容的html元素,如图6-3所示: 

6-3.gif

图6-3 PetShop 4.0的Master Page

@Master指令的定义如下:

< %@ Master  Language ="C#"  AutoEventWireup ="true"  CodeFile ="MasterPage.master.cs"  Inherits ="PetShop.Web.MasterPage"  % >

Master Page同样利用codebehind技术,以PetShop 4.0的Master Page为例,codebehind的代码放在文件MasterPage.master.cs中:

public partial class MasterPage : System.Web.UI.MasterPage {

    private const string HEADER_PREFIX = ".NET Pet Shop :: {0}";

    protected void Page_PreRender(object sender, EventArgs e) { 
        ltlHeader.Text = Page.Header.Title;
        Page.Header.Title = string.Format(HEADER_PREFIX, Page.Header.Title);          
    }
    protected void btnSearch_Click(object sender, EventArgs e) {
        WebUtility.SearchRedirect(txtSearch.Text);    
    }
}

注意Master Page页面不再继承自System.Web.UI.Page,而是继承System.Web.UI.MasterPage类。与Page类继承TemplateControl类不同,它是UserControl类的子类。因此,可以应用在Master Page上的有效指令与UserControl的可用指令相同,例如AutoEventWireup、ClassName、CodeFile、EnableViewState、WarningLevel等。

每一个与Master Page相关的内容页必须在@Page指令的MasterPageFile属性中引用相关的Master Page。例如PetShop 4.0中的CheckOut内容页,其@Page指令的定义如下:

< %@ Page  Language ="C#"  MasterPageFile ="~/MasterPage.master"  AutoEventWireup ="true"  CodeFile ="CheckOut.aspx.cs"  Inherits ="PetShop.Web.CheckOut"  Title ="Check Out"  % >

Master Page可以进行嵌套,例如我们建立了父Master Page页面Parent.master,那么在子Master Page中,可以利用master属性指定其父MasterPage:
<%@ Master Language="C#" master="Parent.master"%>

而内容页则可以根据情况指向Parent.master或者Child.master页面。

虽然说Master Page大部分情况下是以声明方式创建,但我们也可以建立一个类继承System.Web.UI.MasterPage,从而完成对Master Page的编程式创建。但在采用这种方式的同时,应该同时创建.master文件。此外对Master Page的调用也可以利用编程的方式完成,例如动态地添加Master Page,我们重写内容页的Page_PreInit()方法,如下所示:

void  Page_PreInit(Object sender, EventArgs e)
{
    
this.MasterPageFile = "~/NewMaster.master";
}

之所以重写Page_PreInit()方法,是因为Master Page会在内容页初始化阶段进行合并,也即是说是在PreInit阶段完成Master Page的分配。
ASP.NET 2.0引入的新特性,并不仅仅限于上述介绍的内容。例如Theme、Wizard控件等新特性在PetShop 4.0中也得到了大量的应用。虽然ASP.NET 2.0及时地推陈出新,对表示层的设计有所改善,然而作为ASP.NET 2.0的其中一部分,它们仅仅是对现有框架缺失的弥补与改进,属于“锦上添花”的范畴,对于整个表示层设计技术而言,起到的推动作用却非常有限。

直到AJAX(Asynchronous JavaScript and XML)的出现,整个局面才大为改观。虽然AJAX技术带有几分“旧瓶装新酒”的味道,然而它从诞生之初,就具备了王者气象,大有席卷天下之势。各种支持AJAX技术的框架如雨后春笋般纷纷吐出新芽,支撑起百花齐放的繁荣,气势汹汹地营造出唯AJAX独尊的态势。如今,AJAX已经成为了Web应用的主流开发技术,许多业界大鳄都呲牙咧嘴开始了对这一块新领地的抢滩登陆。例如IBM、Oracle、Yahoo等公司都纷纷启动了开源的AJAX项目。微软也不甘落后,及时地推出了ASP.NET AJAX,这是一个基于ASP.NET的AJAX框架,它包括了ASP.NET AJAX服务端组件和ASP.NET AJAX客户端组件,并集成在Visual Studio中,为ASP.NET开发者提供了一个强大的AJAX应用环境。

我现在还无法预知AJAX技术在未来的走向,然而单单从表示层设计的角度而言,AJAX技术亦然带了一场全新的革命。我们或者可以期待未来的PetShop 5.0,可以在表示层设计上带来更多的惊喜。


 
一、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类型的数组?
关键是系统架构和代码学习两方面,对初学和提高有很大帮助 petshop5.0比较大,代码已经解压出来 4.0和3.0没有解压出来,自行安装解压(需要SqlServer数据做连接或者在安装到数据库连接时直接拷贝出来) petshop5.0 基于.NET Framework 3.5 ------------ 使用LINQ to SQL改进数据访问层 PetShop.Model.DataContext.MSPetShop4DataContext 继承System.Data.Linq.DataContext PetShop.Model.ProductInfo与PetShop.Model.CategoryInfo实体类分别映射数据库表 PetShop.Model.ProductInfo其中的Category属性存在一对一的关系 PetShop.Model.CategoryInfo中的Products属性存在一对多的关系 使用WCF来提供RSS, web/FeedService.svc目录下 PetShop.SyndicationFeeds 并在UI层上做一些改进,如使用ASP.NET AJAX,ListView控件等。 在PetShop 5.0中引入了异步处理机制。 插入订单的策略可以分为同步和异步,两者的插入策略明显不同,但对于调用者而言,插入订单的接口是完全一样的,所以PetShop 5.0中设计了IBLLStrategy模块。 虽然在IBLLStrategy模块中,仅仅是简单的IOrderStategy,但同时也给出了一个范例和信息,那就是在业务逻辑的处理中,如果存在业务操作的多样化,或者是今后可能的变化,均应利用抽象的原理。或者使用接口,或者使用抽象类,从而脱离对具体业务的依赖。 不过在PetShop中,由于业务逻辑相对简单,这种思想体现得不够明显。 也正因为此,PetShop将核心的业务逻辑都放到了一个模块BLL中,并没有将具体的实现和抽象严格的按照模块分开。所以表示层和业务逻辑层之间的调用关系,其耦合度相对较高: PetShop4.0源代码 .NET Pet Shop4 应用程序的设计说明了构建企业 n 层 .NET 2.0 应用程序的最佳做法,这种应用程序可能需要支持各种数据库平台和部署方案。 .NET Pet Shop 4 项目的目标是: 工作效率:减少了 .NET Pet Shop 3 的代码数量 - 我们减少了近 25% 的代码。 利用 ASP.NET 2.0 的新功能 - 我们利用母版页、成员身份和配置文件,并设计出一个新的、吸引人的用户界面。 企业体系结构:构建一个灵活的最佳做法应用程序 - 我们实现了设计模式,以及表示层、业务层和数据层的分离。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值