PetShop之表示层设计。PetShop之表示层设计 – 《解剖PetShop》系列的六。

六 PetShop之表示层设计,因而我们可以利用Composite模式来设计View对象

《解剖PetShop》系列的六

六 PetShop之表示层设计

六 PetShop之表示层设计

表示层(Presentation
Layer)的宏图好给系统客户最直接的体验及最好足的信念。正而人口同丁的交接相识一样,初次见面的感到总是永难忘怀的。一宗交给给客户利用的活,如果当用户界面(User
Interface,UI)上少吸引人口之性状,界面不和谐,操作不够体贴,即使这桩产品特性特别不错,架构设计合理,业务逻辑都满足了客户之求,却仍然难以讨得客户之欢心。俗语说:“佛要金装,人要衣装”,特别是对Web应用程序而言,Web网页就哼于食指的衣装,代表正全套系统的身份以及脸面,是揽客“顾客”的尽酷卖点。

表示层(Presentation
Layer)的计划性好被系统客户最直白的感受与无限足的信念。正使人口及人之交接相识一样,初次见面的痛感总是永难忘怀的。一件交给给客户使用的成品,如果当用户界面(User
Interface,UI)上缺少吸引人口的性状,界面不谐和,操作不够体贴,即使就宗产品特性非常漂亮,架构设计合理,业务逻辑都满足了客户之需求,却仍难以讨得客户的欢心。俗语说:“佛要金装,人要衣装”,特别是对于Web应用程序而言,Web网页就吓于食指之服装,代表正在布满体系的身价与脸面,是揽客“顾客”的顶深卖点。

“献丑不如藏拙”,作为艺术细胞缺乏的本身,并无打算于用户界面的美术设计上大做文章,是盖本书略过不领取。本章所关注的意味层设计,还是因为架构设计的角度,阐述于象征层设计被对模式的采用,ASP.NET控件的设计与以,同时还连了针对ASP.NET
2.0初特点的介绍。

“献丑不如藏拙”,作为艺术细胞缺乏的自我,并无打算于用户界面的美术设计上大做文章,是为本书略过不取。本章所关注的表示层设计,还是以架构设计的角度,阐述于象征层设计着对模式的用,ASP.NET控件的规划及使用,同时还连了针对性ASP.NET
2.0初特性的牵线。

6.1  MVC模式

6.1  MVC模式

意味着层设计被最好要之模式是MVC(Model-View-Controller,即模型-视图-控制器)模式。MVC模式极其早是出于SmallTalk语言研究团提出的,被广泛应用在用户交互应用程序中。Controller根据用户请求(Response)修改Model的性,此时Event(事件)被硌,所有因让Model的View对象见面自动更新,并根据Model对象来一个响应(Response)信息,返回给Controller。Martin
Fowler以《企业应用架构模式》一书写被,展示了MVC模式应用之全都经过,如图6-1所出示: 

代表层设计中最为根本之模式是MVC(Model-View-Controller,即模型-视图-控制器)模式。MVC模式极其早是由SmallTalk语言研究团提出的,被广泛应用在用户交互应用程序中。Controller根据用户要(Response)修改Model的性,此时Event(事件)被触发,所有乘让Model的View对象会自动更新,并因Model对象来一个响应(Response)信息,返回给Controller。Martin
Fowler以《企业应用架构模式》一题中,展示了MVC模式采用的全都经过,如图6-1所展示: 

图片 1

图片 2

图6-1 典型的MVC模式

图6-1 典型的MVC模式

倘以MVC模式拆解为老三独独立的组成部分:Model、View、Controller,我们得以经GOF设计模式来落实与保管它们中间的关系。在系统架构设计中,业务逻辑层的小圈子对象和数访问层的数据值对象还属于MVC模式之Model对象。如果只要保管Model与View之间的关联,可以利用Observer模式,View作为观察者,一旦Model的属于性值发生变化,就会通知View基于Model的价进行创新。而Controller作为控制用户要/响应的对象,则足以利用Mediator模式,专门负责请求/响应任务中的调剂。而对此View本身,在面向组件设计思想的底子及,我们通常以它计划吧组件或者控件,这些零部件或者控件根据我特点的异,共同做一种类似于递归组合的目标组织,因而我们得以使用Composite模式来统筹View对象。

假设用MVC模式拆解为老三独单身的片段:Model、View、Controller,我们好由此GOF设计模式来促成与治本它们中的关联。在网架构设计中,业务逻辑层的世界对象同数据访问层的数据值对象都属MVC模式的Model对象。如果一旦管制Model与View之间的涉及,可以应用Observer模式,View作为观察者,一旦Model的属于性值发生变化,就会通知View基于Model的价进行创新。而Controller作为控制用户请求/响应的对象,则足以应用Mediator模式,专门负责请求/响应任务中的调剂。而对此View本身,在面向组件设计思想的底蕴及,我们便以它设计吧组件或者控件,这些零件或者控件根据自身特色的不同,共同整合一栽恍若于递归组合的靶子组织,因而我们可应用Composite模式来设计View对象。

唯独在.NET平台下,我们并不需要自己失去落实MVC模式。对于View对象而言,ASP.NET已经提供了常用之Web控件,我们呢得经过连续System.Web.UI.UserControl,自定义用户控件,并使用ASPX页面组合Web控件来兑现视图。ASP.NET定义了System.Web.UI.Page类,它一定给MVC模式的Controller对象,可以处理用户的要。由于用了codebehind技术,使得用户界面的显得与UI实现逻辑完全分开,也不怕凡说,View对象与Controller对象成相对独立的蝇头有,从而利于代码的重用性。比较ASP而言,这种编程方式又称开发人员的编程习惯,同时方便开发人员与UI设计人员之分工及搭档。至于Model对象,则为业务逻辑层的领域对象。此外,.NET平台经过ADO.NET提供了DataSet对象,便于与Web控件的数据源绑定。

只是在.NET平台下,我们并不需要自己失去落实MVC模式。对于View对象而言,ASP.NET已经提供了常用的Web控件,我们也可以通过持续System.Web.UI.UserControl,自定义用户控件,并采用ASPX页面组合Web控件来兑现视图。ASP.NET定义了System.Web.UI.Page类,它一定给MVC模式的Controller对象,可以拍卖用户之求。由于采用了codebehind技术,使得用户界面的亮和UI实现逻辑完全分离,也即凡是说,View对象及Controller对象变成相对独立的个别有,从而有利于代码的重用性。比较ASP而言,这种编程方式又称开发人员的编程习惯,同时方便开发人员与UI设计人员的分工及搭档。至于Model对象,则也作业逻辑层的园地对象。此外,.NET平台经过ADO.NET提供了DataSet对象,便于与Web控件的数据源绑定。

6.2  Page Controller模式的用

6.2  Page Controller模式的使

综观PetShop的表示层设计,充分利用了ASP.NET的技能特色,通过Web页面与用户控件控制以及呈现视图,并动用codebehind技术以事情逻辑层的圈子对象加入到代表层实现逻辑中,一个天下无双的Page
Controller模式呼之欲出。

纵观PetShop的象征层设计,充分利用了ASP.NET的技巧特点,通过Web页面与用户控件控制和显现视图,并行使codebehind技术将工作逻辑层的天地对象在到代表层实现逻辑中,一个典型的Page
Controller模式呼之欲出。

Page Controller模式是Martin
Fowler在《企业应用架构模式》中尽关键之代表层模式有。在.NET平台下,Page
Controller模式的贯彻非常简单,以Products.aspx页面吗条例。首先以aspx页面被,进行如下的设置:

Page Controller模式是Martin
Fowler在《企业应用架构模式》中尽重点之代表层模式有。在.NET平台下,Page
Controller模式的落实非常简单,以Products.aspx页面也例。首先在aspx页面被,进行如下的安装:

图片 3<%@ Page AutoEventWireup=”true” Language=”C#” MasterPageFile=”~/MasterPage.master” Title=”Products” Inherits=”PetShop.Web.Products” CodeFile=”~/Products.aspx.cs” %>

图片 4<%@ Page AutoEventWireup=”true” Language=”C#” MasterPageFile=”~/MasterPage.master” Title=”Products” Inherits=”PetShop.Web.Products” CodeFile=”~/Products.aspx.cs” %>

Aspx页面继承自System.Web.UI.Page类。Page类对象通过连续System.Web.UI.Control类,从而拥有了Web控件的性状,同时她还落实了IHttpHandler接口。作为ASP.NET处理HTTP
Web请求的接口,提供了如下的概念:

Aspx页面继承自System.Web.UI.Page类。Page类对象通过连续System.Web.UI.Control类,从而拥有了Web控件的特色,同时她还落实了IHttpHandler接口。作为ASP.NET处理HTTP
Web请求的接口,提供了如下的概念:

图片 5[AspNetHostingPermission(SecurityAction.InheritanceDemand, 
图片 6Level=AspNetHostingPermissionLevel.Minimal), 
图片 7AspNetHostingPermission(SecurityAction.LinkDemand, 
图片 8Level=AspNetHostingPermissionLevel.Minimal)]
图片 9public interface IHttpHandler
图片 10图片 11图片 12{
图片 13      void ProcessRequest(HttpContext context);
图片 14图片 15      bool IsReusable 图片 16{ get; }
图片 17}
图片 18

图片 19[AspNetHostingPermission(SecurityAction.InheritanceDemand, 
图片 20Level=AspNetHostingPermissionLevel.Minimal), 
图片 21AspNetHostingPermission(SecurityAction.LinkDemand, 
图片 22Level=AspNetHostingPermissionLevel.Minimal)]
图片 23public interface IHttpHandler
图片 24图片 25图片 26{
图片 27      void ProcessRequest(HttpContext context);
图片 28图片 29      bool IsReusable 图片 30{ get; }
图片 31}
图片 32

Page类实现了ProcessRequest()方法,通过它好装Page对象的Request和Response属性,从而成就对用户要/相应的操纵。然后Page类通过从Control类继承来的Load事件,将View与Model建立涉,如Products.aspx.cs所示:

Page类实现了ProcessRequest()方法,通过她好装Page对象的Request和Response属性,从而就对用户请求/相应的操纵。然后Page类通过自Control类继承来的Load事件,将View与Model建立关系,如Products.aspx.cs所示:

图片 33public partial class Products : System.Web.UI.Page 
图片 34图片 35图片 36{
图片 37    protected void Page_Load(object sender, EventArgs e) 
图片 38图片 39    图片 40{
图片 41        //get page header and title
图片 42        Page.Title = WebUtility.GetCategoryName(Request.QueryString[“categoryId”]);
图片 43    }
图片 44}
图片 45

图片 46public partial class Products : System.Web.UI.Page 
图片 47图片 48图片 49{
图片 50    protected void Page_Load(object sender, EventArgs e) 
图片 51图片 52    图片 53{
图片 54        //get page header and title
图片 55        Page.Title = WebUtility.GetCategoryName(Request.QueryString[“categoryId”]);
图片 56    }
图片 57}
图片 58

事件机制正是observer模式的贯彻,当ASPX页面的Load事件为激起后,系统通过WebUtility类(在第28回中发出对WebUtility类的详实介绍)的GetCategoryName()方法,获得Category值,并拿该出示在页面的Title上。Page对象作为Controller,就哼似一个调停者,用于协调View与Model之间的关联。

事件机制正是observer模式的实现,当ASPX页面的Load事件为激起后,系统通过WebUtility类(在第28回中起指向WebUtility类的详细介绍)的GetCategoryName()方法,获得Category值,并以那个形在页面的Title上。Page对象作为Controller,就好似一个调停者,用于协调View与Model之间的涉及。

鉴于ASPX页面被尚好分包Web控件,这些控件对象同是作为View对象,通过Page类型对象就对它们的支配。例如当CheckOut.aspx页面被,当用户发生CheckOut的求后,作为System.Web.UI.WebControls.Winzard控件类型的wzdCheckOut,会于所有向导过程结束时,触发FinishButtonClick事件,并在拖欠事件被调用领域对象Order的Insert()方法,如下所示:

出于ASPX页面被尚可以蕴涵Web控件,这些控件对象同是作View对象,通过Page类型对象好对它的控制。例如在CheckOut.aspx页面中,当用户产生CheckOut的要后,作为System.Web.UI.WebControls.Winzard控件类型的wzdCheckOut,会当方方面面向导过程结束时,触发FinishButtonClick事件,并当该事件备受调用领域对象Order的Insert()方法,如下所示:

图片 59public partial class CheckOut : System.Web.UI.Page图片 60
图片 61
图片 62图片 63    protected void wzdCheckOut_FinishButtonClick(object sender, WizardNavigationEventArgs e) 图片 64{
图片 65图片 66        if (Profile.ShoppingCart.CartItems.Count > 0) 图片 67{
图片 68图片 69            if (Profile.ShoppingCart.Count > 0) 图片 70{
图片 71
图片 72                // display ordered items
图片 73                CartListOrdered.Bind(Profile.ShoppingCart.CartItems);
图片 74
图片 75                // display total and credit card information
图片 76                ltlTotalComplete.Text = ltlTotal.Text;
图片 77                ltlCreditCardComplete.Text = ltlCreditCard.Text;
图片 78
图片 79                // create order
图片 80                OrderInfo order = new OrderInfo(int.MinValue, DateTime.Now, User.Identity.Name, GetCreditCardInfo(), billingForm.Address, shippingForm.Address, Profile.ShoppingCart.Total, Profile.ShoppingCart.GetOrderLineItems(), null);
图片 81
图片 82                // insert
图片 83                Order newOrder = new Order();
图片 84                newOrder.Insert(order);
图片 85
图片 86                // destroy cart
图片 87                Profile.ShoppingCart.Clear();
图片 88                Profile.Save();
图片 89            }
图片 90        }
图片 91图片 92        else 图片 93{
图片 94            lblMsg.Text = “<p><br>Can not process the order. Your cart is empty.</p><p class=SignUpLabel><a class=linkNewUser href=Default.aspx>Continue shopping</a></p>”;
图片 95            wzdCheckOut.Visible = false;
图片 96        }
图片 97    }
图片 98
图片 99

图片 100public partial class CheckOut : System.Web.UI.Page图片 101
图片 102
图片 103图片 104    protected void wzdCheckOut_FinishButtonClick(object sender, WizardNavigationEventArgs e) 图片 105{
图片 106图片 107        if (Profile.ShoppingCart.CartItems.Count > 0) 图片 108{
图片 109图片 110            if (Profile.ShoppingCart.Count > 0) 图片 111{
图片 112
图片 113                // display ordered items
图片 114                CartListOrdered.Bind(Profile.ShoppingCart.CartItems);
图片 115
图片 116                // display total and credit card information
图片 117                ltlTotalComplete.Text = ltlTotal.Text;
图片 118                ltlCreditCardComplete.Text = ltlCreditCard.Text;
图片 119
图片 120                // create order
图片 121                OrderInfo order = new OrderInfo(int.MinValue, DateTime.Now, User.Identity.Name, GetCreditCardInfo(), billingForm.Address, shippingForm.Address, Profile.ShoppingCart.Total, Profile.ShoppingCart.GetOrderLineItems(), null);
图片 122
图片 123                // insert
图片 124                Order newOrder = new Order();
图片 125                newOrder.Insert(order);
图片 126
图片 127                // destroy cart
图片 128                Profile.ShoppingCart.Clear();
图片 129                Profile.Save();
图片 130            }
图片 131        }
图片 132图片 133        else 图片 134{
图片 135            lblMsg.Text = “<p><br>Can not process the order. Your cart is empty.</p><p class=SignUpLabel><a class=linkNewUser href=Default.aspx>Continue shopping</a></p>”;
图片 136            wzdCheckOut.Visible = false;
图片 137        }
图片 138    }
图片 139
图片 140

在点的平段落代码中,非常突出地发表了Model与View之间的干。它经过取得控件的属于性值,作为参数值传递让数据值对象OrderInfo,从而以页面上出的订单信息创建订单对象,然后还调用领域对象Order的Inser()方法将OrderInfo对象插入到数表中。此外,它还对天地对象ShoppingCart的多寡项作出判断,如果其值等于0,就于页面被显示UI提示信息。此时,View的内容决定了Model的值,而Model值反过来又控制了View的显示内容。

以面的同段代码中,非常典型地表达了Model与View之间的涉。它通过获取控件的属性值,作为参数值传递给数据值对象OrderInfo,从而采取页面及发生的订单信息创建订单对象,然后再调用领域对象Order的Inser()方法将OrderInfo对象插入到多少表中。此外,它还对天地对象ShoppingCart的数码项作出判断,如果其值等于0,就以页面被显示UI提示信息。此时,View的情决定了Model的价值,而Model值反过来还要控制了View的展示内容。

6.3  ASP.NET控件

6.3  ASP.NET控件

ASP.NET控件是View对象极其要的一部分,它充分利用了面向对象的筹划思想,通过包与继承构建一个个控件对象,使得用户以开Web页面时,能够用这些控件,甚至从定义自己的控件。在第8章节中,我曾经介绍了.NET
Framework中控件的统筹思想,通过引入一种植“复合方式”的Composite模式实现了控件树。在ASP.NET控件被,System.Web.UI.Control就是即时棵控件树的到底,它定义了有着ASP.NET控件共有的性质、方法与事件,并负责管理和控制控件的万事实施生命周期。

ASP.NET控件是View对象极其要的有的,它充分利用了面向对象的规划思想,通过包与后续构建一个个控件对象,使得用户以出Web页面时,能够用这些控件,甚至打定义自己的控件。在第8章节中,我曾经介绍了.NET
Framework中控件的统筹思想,通过引入一种“复合方式”的Composite模式实现了控件树。在ASP.NET控件被,System.Web.UI.Control就是当下株控件树的彻底,它定义了富有ASP.NET控件共有的性质、方法与波,并负责管理和决定控件的整实施生命周期。

Control基类并没有含UI的特定功能,如果急需提供以及UI相关的章程属性,就用打System.Web.UI.WebControls.WebControl类派生。该类实际上也是Control类的子类,但其附加了像ForeColor、BackColor、Font等属性。

Control基类并没有含UI的特定功能,如果急需提供与UI相关的措施属性,就得由System.Web.UI.WebControls.WebControl类派生。该类实际上也是Control类的子类,但她附加了像ForeColor、BackColor、Font等特性。

除却,还有一个重要之好像是System.Web.UI.UserControl,即用户控件类,它一律是Control类的子类。我们好打定义有用户控件派生自UserControl,在Visual
Studio的Design环境下,我们得经拖动控件的法以多种类型的控件组合成一个自定义用户控件,也可以以codebehind方式下,为自定义用户控件类添加新的性质与办法。

除外,还有一个最主要的类是System.Web.UI.UserControl,即用户控件类,它同样是Control类的子类。我们好起定义有用户控件派生自UserControl,在Visual
Studio的Design环境下,我们好由此拖动控件的计拿多种类型的控件组合成一个自定义用户控件,也足以当codebehind方式下,为从定义用户控件类添加新的特性和措施。

全方位ASP.NET控件类的层次结构如图6-2所展示: 

全体ASP.NET控件类的层次结构如图6-2所著: 

图片 141

图片 142

图6-2 ASP.NET控件类的层次结构

希冀6-2 ASP.NET控件类的层次结构

ASP.NET控件的履生命周期如表6-1所著:

ASP.NET控件的推行生命周期如表6-1所出示:

阶段

控件需要执行的操作
要重写的方法或事件
初始化 初始化在传入 Web 请求生命周期内所需的设置。 Init 事件(OnInit 方法)
加载视图状态 在此阶段结束时,就会自动填充控件的 ViewState 属性,控件可以重写 LoadViewState 方法的默认实现,以自定义状态还原。 LoadViewState 方法
处理回发数据 处理传入窗体数据,并相应地更新属性。
注意:只有处理回发数据的控件参与此阶段。
LoadPostData 方法(如果已实现 IPostBackDataHandler)
加载 执行所有请求共有的操作,如设置数据库查询。此时,树中的服务器控件已创建并初始化、状态已还原并且窗体控件反映了客户端的数据。 Load 事件(OnLoad 方法)
发送回发更改通知 引发更改事件以响应当前和以前回发之间的状态更改。
注意:只有引发回发更改事件的控件参与此阶段。
RaisePostDataChangedEvent 方法(如果已实现 IPostBackDataHandler)
处理回发事件 处理引起回发的客户端事件,并在服务器上引发相应的事件。
注意:只有处理回发事件的控件参与此阶段。
RaisePostBackEvent 方法(如果已实现 IPostBackEventHandler)
预呈现 在呈现输出之前执行任何更新。可以保存在预呈现阶段对控件状态所做的更改,而在呈现阶段所对的更改则会丢失。 PreRender 事件(OnPreRender 方法)
保存状态 在此阶段后,自动将控件的 ViewState 属性保持到字符串对象中。此字符串对象被发送到客户端并作为隐藏变量发送回来。为了提高效率,控件可以重写 SaveViewState 方法以修改 ViewState 属性。 SaveViewState 方法
呈现 生成呈现给客户端的输出。 Render 方法
处置 执行销毁控件前的所有最终清理操作。在此阶段必须释放对昂贵资源的引用,如数据库链接。 Dispose 方法
卸载 执行销毁控件前的所有最终清理操作。控件作者通常在 Dispose 中执行清除,而不处理此事件。 UnLoad 事件(On UnLoad 方法)

阶段

控件需要执行的操作
要重写的方法或事件
初始化 初始化在传入 Web 请求生命周期内所需的设置。 Init 事件(OnInit 方法)
加载视图状态 在此阶段结束时,就会自动填充控件的 ViewState 属性,控件可以重写 LoadViewState 方法的默认实现,以自定义状态还原。 LoadViewState 方法
处理回发数据 处理传入窗体数据,并相应地更新属性。
注意:只有处理回发数据的控件参与此阶段。
LoadPostData 方法(如果已实现 IPostBackDataHandler)
加载 执行所有请求共有的操作,如设置数据库查询。此时,树中的服务器控件已创建并初始化、状态已还原并且窗体控件反映了客户端的数据。 Load 事件(OnLoad 方法)
发送回发更改通知 引发更改事件以响应当前和以前回发之间的状态更改。
注意:只有引发回发更改事件的控件参与此阶段。
RaisePostDataChangedEvent 方法(如果已实现 IPostBackDataHandler)
处理回发事件 处理引起回发的客户端事件,并在服务器上引发相应的事件。
注意:只有处理回发事件的控件参与此阶段。
RaisePostBackEvent 方法(如果已实现 IPostBackEventHandler)
预呈现 在呈现输出之前执行任何更新。可以保存在预呈现阶段对控件状态所做的更改,而在呈现阶段所对的更改则会丢失。 PreRender 事件(OnPreRender 方法)
保存状态 在此阶段后,自动将控件的 ViewState 属性保持到字符串对象中。此字符串对象被发送到客户端并作为隐藏变量发送回来。为了提高效率,控件可以重写 SaveViewState 方法以修改 ViewState 属性。 SaveViewState 方法
呈现 生成呈现给客户端的输出。 Render 方法
处置 执行销毁控件前的所有最终清理操作。在此阶段必须释放对昂贵资源的引用,如数据库链接。 Dispose 方法
卸载 执行销毁控件前的所有最终清理操作。控件作者通常在 Dispose 中执行清除,而不处理此事件。 UnLoad 事件(On UnLoad 方法)

表6-1 ASP.NET控件的实践生命周期

表明6-1 ASP.NET控件的尽生命周期

每当这里,控件设计以了Template
Method模式,Control基类提供了绝大多数protected虚方法,留得其子类改写那道。以PetShop
4.0乎条例,就定义了有限独ASP.NET控件,它们还属于System.Web.UI.WebControls.WebControl的子类。其中,CustomList控件派生自System.Web.UI.WebControls.DataList,CustomGrid控件则派生自System.Web.UI.WebControls.Repeater。

以这边,控件设计下了Template
Method模式,Control基类提供了绝大多数protected虚方法,留得其子类改写那法。以PetShop
4.0为条例,就定义了片独ASP.NET控件,它们都属System.Web.UI.WebControls.WebControl的子类。其中,CustomList控件派生自System.Web.UI.WebControls.DataList,CustomGrid控件则派生自System.Web.UI.WebControls.Repeater。

是因为当下半单控件都更改了彼父类控件的显现方式,故而,我们可以经过更写父类的Render虚方法,完成控件的自定义。例如CustomGrid控件:

出于这点儿独控件都转移了那父类控件的展现方式,故而,我们可通过再写父类的Render虚方法,完成控件的自定义。例如CustomGrid控件:

图片 143public class CustomGrid : Repeater…
图片 144//Static constants
图片 145    protected const string HTML1 = “<table cellpadding=0 
图片 146cellspacing=0><tr><td colspan=2>”;
图片 147    protected const string HTML2 = “</td></tr><tr><td class=paging align=left>”;
图片 148    protected const string HTML3 = “</td><td align=right class=paging>”;
图片 149    protected const string HTML4 = “</td></tr></table>”;
图片 150    private static readonly Regex RX = new Regex(@”^&page=\d+”, 
图片 151RegexOptions.Compiled);
图片 152    private const string LINK_PREV = “<a href=?page={0}>< Previous</a>”;
图片 153    private const string LINK_MORE = “<a href=?page={0}>More ></a>”;
图片 154private const string KEY_PAGE = “page”;
图片 155    private const string COMMA = “?”;
图片 156    private const string AMP = “&”;
图片 157
图片 158图片 159override protected void Render(HtmlTextWriter writer) 图片 160{
图片 161
图片 162        //Check there is some data attached
图片 163图片 164        if (ItemCount == 0) 图片 165{
图片 166            writer.Write(emptyText);
图片 167            return;
图片 168        }
图片 169        //Mask the query
图片 170        string query = Context.Request.Url.Query.Replace(COMMA, AMP);
图片 171        query = RX.Replace(query, string.Empty);
图片 172        // Write out the first part of the control, the table header
图片 173        writer.Write(HTML1);
图片 174        // Call the inherited method
图片 175        base.Render(writer);
图片 176        // Write out a table row closure
图片 177        writer.Write(HTML2);
图片 178        //Determin whether next and previous buttons are required
图片 179        //Previous button?
图片 180        if (currentPageIndex > 0)
图片 181            writer.Write(string.Format(LINK_PREV, (currentPageIndex – 1) + query));
图片 182        //Close the table data tag
图片 183        writer.Write(HTML3);
图片 184
图片 185        //Next button?
图片 186        if (currentPageIndex < PageCount)
图片 187            writer.Write(string.Format(LINK_MORE, (currentPageIndex + 1) + query));
图片 188
图片 189        //Close the table
图片 190        writer.Write(HTML4);
图片 191    }

图片 192public class CustomGrid : Repeater…
图片 193//Static constants
图片 194    protected const string HTML1 = “<table cellpadding=0 
图片 195cellspacing=0><tr><td colspan=2>”;
图片 196    protected const string HTML2 = “</td></tr><tr><td class=paging align=left>”;
图片 197    protected const string HTML3 = “</td><td align=right class=paging>”;
图片 198    protected const string HTML4 = “</td></tr></table>”;
图片 199    private static readonly Regex RX = new Regex(@”^&page=\d+”, 
图片 200RegexOptions.Compiled);
图片 201    private const string LINK_PREV = “<a href=?page={0}>< Previous</a>”;
图片 202    private const string LINK_MORE = “<a href=?page={0}>More ></a>”;
图片 203private const string KEY_PAGE = “page”;
图片 204    private const string COMMA = “?”;
图片 205    private const string AMP = “&”;
图片 206
图片 207图片 208override protected void Render(HtmlTextWriter writer) 图片 209{
图片 210
图片 211        //Check there is some data attached
图片 212图片 213        if (ItemCount == 0) 图片 214{
图片 215            writer.Write(emptyText);
图片 216            return;
图片 217        }
图片 218        //Mask the query
图片 219        string query = Context.Request.Url.Query.Replace(COMMA, AMP);
图片 220        query = RX.Replace(query, string.Empty);
图片 221        // Write out the first part of the control, the table header
图片 222        writer.Write(HTML1);
图片 223        // Call the inherited method
图片 224        base.Render(writer);
图片 225        // Write out a table row closure
图片 226        writer.Write(HTML2);
图片 227        //Determin whether next and previous buttons are required
图片 228        //Previous button?
图片 229        if (currentPageIndex > 0)
图片 230            writer.Write(string.Format(LINK_PREV, (currentPageIndex – 1) + query));
图片 231        //Close the table data tag
图片 232        writer.Write(HTML3);
图片 233
图片 234        //Next button?
图片 235        if (currentPageIndex < PageCount)
图片 236            writer.Write(string.Format(LINK_MORE, (currentPageIndex + 1) + query));
图片 237
图片 238        //Close the table
图片 239        writer.Write(HTML4);
图片 240    }

鉴于CustomGrid继承自Repeater控件,因而它们同时还继承了Repeater的DataSource属性,这是一个虚属性,它默认的set访问器属性如下:

出于CustomGrid继承自Repeater控件,因而它们同时还累了Repeater的DataSource属性,这是一个虚属性,它默认的set访问器属性如下:

图片 241public virtual object DataSource
图片 242图片 243图片 244{
图片 245图片 246      get  图片 247{… }
图片 248      set
图片 249图片 250      图片 251{
图片 252            if (((value != null) && !(value is IListSource)) && !(value is IEnumerable))
图片 253图片 254            图片 255{
图片 256图片 257                  throw new ArgumentException(SR.GetString(“Invalid_DataSource_Type”, new object[] 图片 258{ this.ID }));
图片 259            }
图片 260            this.dataSource = value;
图片 261            this.OnDataPropertyChanged();
图片 262      }
图片 263}

图片 264public virtual object DataSource
图片 265图片 266图片 267{
图片 268图片 269      get  图片 270{… }
图片 271      set
图片 272图片 273      图片 274{
图片 275            if (((value != null) && !(value is IListSource)) && !(value is IEnumerable))
图片 276图片 277            图片 278{
图片 279图片 280                  throw new ArgumentException(SR.GetString(“Invalid_DataSource_Type”, new object[] 图片 281{ this.ID }));
图片 282            }
图片 283            this.dataSource = value;
图片 284            this.OnDataPropertyChanged();
图片 285      }
图片 286}

于CustomGrid而言,DataSource属性有着不同的安行为,因而在定义CustomGrid控件的时光,需要改变写DataSource虚属性,如下所示:

对CustomGrid而言,DataSource属性有着不同之安行为,因而在定义CustomGrid控件的时光,需要转移写DataSource虚属性,如下所示:

图片 287private IList dataSource;
图片 288private int itemCount;
图片 289
图片 290图片 291override public object DataSource 图片 292{
图片 293图片 294    set 图片 295{
图片 296    //This try catch block is to avoid issues with the VS.NET designer
图片 297        //The designer will try and bind a datasource which does not derive from ILIST
图片 298图片 299        try 图片 300{
图片 301            dataSource = (IList)value;
图片 302            ItemCount = dataSource.Count;
图片 303        }
图片 304图片 305        catch 图片 306{
图片 307            dataSource = null;
图片 308            ItemCount = 0;
图片 309        }
图片 310    }
图片 311}

图片 312private IList dataSource;
图片 313private int itemCount;
图片 314
图片 315图片 316override public object DataSource 图片 317{
图片 318图片 319    set 图片 320{
图片 321    //This try catch block is to avoid issues with the VS.NET designer
图片 322        //The designer will try and bind a datasource which does not derive from ILIST
图片 323图片 324        try 图片 325{
图片 326            dataSource = (IList)value;
图片 327            ItemCount = dataSource.Count;
图片 328        }
图片 329图片 330        catch 图片 331{
图片 332            dataSource = null;
图片 333            ItemCount = 0;
图片 334        }
图片 335    }
图片 336}

当装的value对象值未也IList类型时,set访问器就将捕获异常,然后用dataSource字段设置为null。

当装的value对象值未呢IList类型时,set访问器就用捕获异常,然后将dataSource字段设置为null。

由于我们改写了DataSource属性,因而改写Repeater类的OnDataBinding()方法也便势在必行。此外,CustomGrid还提供了分页的职能,我们呢需实现分页的连带操作。与DataSource属性不同,Repeater类的OnDataBinding()方法其实是继承和改写了Control基类的OnDataBinding()虚方法,而我辈而在此基础及改写了Repeater类的OnDataBinding()方法:

鉴于我们改写了DataSource属性,因而改写Repeater类的OnDataBinding()方法呢尽管势在必行。此外,CustomGrid还提供了分页的功用,我们吧得贯彻分页的连锁操作。与DataSource属性不同,Repeater类的OnDataBinding()方法其实是延续与改写了Control基类的OnDataBinding()虚方法,而我辈而当这基础及改写了Repeater类的OnDataBinding()方法:

图片 337图片 338override protected void OnDataBinding(EventArgs e) 图片 339{
图片 340
图片 341    //Work out which items we want to render to the page
图片 342    int start = CurrentPageIndex * pageSize;
图片 343    int size = Math.Min(pageSize, ItemCount – start);
图片 344
图片 345    IList page = new ArrayList();
图片 346    //Add the relevant items from the datasource
图片 347    for (int i = 0; i < size; i++)
图片 348        page.Add(dataSource[start + i]);
图片 349
图片 350    //set the base objects datasource
图片 351    base.DataSource = page;
图片 352    base.OnDataBinding(e);
图片 353}

图片 354图片 355override protected void OnDataBinding(EventArgs e) 图片 356{
图片 357
图片 358    //Work out which items we want to render to the page
图片 359    int start = CurrentPageIndex * pageSize;
图片 360    int size = Math.Min(pageSize, ItemCount – start);
图片 361
图片 362    IList page = new ArrayList();
图片 363    //Add the relevant items from the datasource
图片 364    for (int i = 0; i < size; i++)
图片 365        page.Add(dataSource[start + i]);
图片 366
图片 367    //set the base objects datasource
图片 368    base.DataSource = page;
图片 369    base.OnDataBinding(e);
图片 370}

另外,CustomGrid控件类还增了众属于自己之性和措施,例如PageSize、PageCount属性以及SetPage()方法齐。正是因为ASP.NET控件引入了Composite模式以及Template
Method模式,当我们于打定义控件时,就可以经过连续和改写的法来形成控件的规划。自定义ASP.NET控件一方面可因系统的需求实现特定的效用,也会尽充分限度地实现目标的录取,既可减掉编码量,同时为有益未来本着先后的扩充及改。
于PetShop
4.0被,除了由定义了上述WebControl控件的分段控件外,最要紧的或者利用了用户控件。在Controls文件夹下,一共定义了11独用户控件,内容包含客户地址信息、信用卡信息、购物车信息、期望列表(Wish
List)信息与导航信息、搜索结果信息相当。它们相当给是有的组合控件,除了含有了分段控件的主意及总体性外,也定义了有少不了的UI实现逻辑。以ShoppingCartControl用户控件为例,它见面当该控件被展现(Render)之前,做有数额准备工作,获取购物车多少,并作为数据源绑定到其下的Repeater控件:

除此以外,CustomGrid控件类还增了很多属于自己之属性和艺术,例如PageSize、PageCount属性以及SetPage()方法齐。正是因为ASP.NET控件引入了Composite模式以及Template
Method模式,当我们于打定义控件时,就足以经过连续与改写的点子来形成控件的设计。自定义ASP.NET控件一方面可以因网的需要实现特定的机能,也克太深限度地贯彻目标的任用,既好削减编码量,同时也惠及未来对先后的恢宏和改。
以PetShop
4.0负,除了从定义了上述WebControl控件的道岔控件外,最重大的或者利用了用户控件。在Controls文件夹下,一共定义了11个用户控件,内容包含客户地址信息、信用卡信息、购物车消息、期望列表(Wish
List)信息及导航信息、搜索结果信息等。它们相当给是一些构成控件,除了饱含了分层控件的方法以及性外,也定义了一些必需之UI实现逻辑。以ShoppingCartControl用户控件为条例,它会在该控件被展现(Render)之前,做有数目准备干活,获取购物车数量,并视作数据源绑定到其下的Repeater控件:

图片 371public partial class ShoppingCartControl : System.Web.UI.UserControl图片 372
图片 373       
图片 374图片 375    protected void Page_PreRender(object sender, EventArgs e) 图片 376{
图片 377图片 378        if (!IsPostBack) 图片 379{
图片 380            BindCart();                
图片 381        }
图片 382    }
图片 383图片 384    private void BindCart() 图片 385{
图片 386
图片 387        ICollection<CartItemInfo> cart = Profile.ShoppingCart.CartItems;
图片 388图片 389        if (cart.Count > 0) 图片 390{
图片 391            repShoppingCart.DataSource = cart;
图片 392            repShoppingCart.DataBind();
图片 393            PrintTotal();
图片 394            plhTotal.Visible = true;
图片 395        }
图片 396图片 397        else 图片 398{
图片 399            repShoppingCart.Visible = false;
图片 400            plhTotal.Visible = false;
图片 401            lblMsg.Text = “Your cart is empty.”;
图片 402        }
图片 403    }

图片 404public partial class ShoppingCartControl : System.Web.UI.UserControl图片 405
图片 406       
图片 407图片 408    protected void Page_PreRender(object sender, EventArgs e) 图片 409{
图片 410图片 411        if (!IsPostBack) 图片 412{
图片 413            BindCart();                
图片 414        }
图片 415    }
图片 416图片 417    private void BindCart() 图片 418{
图片 419
图片 420        ICollection<CartItemInfo> cart = Profile.ShoppingCart.CartItems;
图片 421图片 422        if (cart.Count > 0) 图片 423{
图片 424            repShoppingCart.DataSource = cart;
图片 425            repShoppingCart.DataBind();
图片 426            PrintTotal();
图片 427            plhTotal.Visible = true;
图片 428        }
图片 429图片 430        else 图片 431{
图片 432            repShoppingCart.Visible = false;
图片 433            plhTotal.Visible = false;
图片 434            lblMsg.Text = “Your cart is empty.”;
图片 435        }
图片 436    }

在ShoppingCart页面下,我们得以加入该用户控件,如下所示:

于ShoppingCart页面下,我们得参加该用户控件,如下所示:

图片 437<PetShopControl:shoppingcartcontrol id=”ShoppingCartControl1″ runat=”server”></PetShopControl:shoppingcartcontrol>

图片 438<PetShopControl:shoppingcartcontrol id=”ShoppingCartControl1″ runat=”server”></PetShopControl:shoppingcartcontrol>

是因为ShoppingCartControl用户控件已经实现了用来呈现购物车数的逻辑,那么在ShoppingCart.aspx.cs中,就得绝不承担这些逻辑,在尽完成目标重用的经过中,同时还要上了职责分开之目的。用户控件的设计者和页面设计者可以互不干扰,分头完成好的规划。特别是于页面设计者而言,他可以是纯净的UI设计人员角色,仅需要关注用户界面是否美观和团结,对于代表层中针对天地对象的调用与操作就好不用理会,整个页面的代码也显示结构清晰、逻辑清楚,无疑也“干净”了成千上万。

由ShoppingCartControl用户控件已经落实了用于呈现购物车多少的逻辑,那么当ShoppingCart.aspx.cs中,就可以不用承担这些逻辑,在尽完成目标重用的长河遭到,同时又上了任务分开的目的。用户控件的设计者和页面设计者可以互不干扰,分头完成好的设计。特别是对于页面设计者而言,他好是单一的UI设计人员角色,仅用关注用户界面是否美观和投机,对于代表层中对世界对象的调用与操作就足以无需理会,整个页面的代码也展示结构清晰、逻辑清楚,无疑也“干净”了广大。

6.4  ASP.NET 2.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之计划性分别介绍她的兑现。

鉴于PetShop 4.0凡基于.NET Framework
2.0阳台支付之电子商务系统,因而其当代表层为引入了众多ASP.NET
2.0底初特色,例如MemberShip、Profile、Master
Page、登录控件等特色。接下来,我以组成PetShop
4.0的计划分别介绍她的落实。

6.4.1  Profile特性

6.4.1  Profile特性

Profile提供的功力是本着用户的个性化服务。在ASP.NET
1.x本子时,我们好采用Session、Cookie等方法来存储用户的状态信息。然而Session对象是具生存期的,一旦生存期结束,该目标保留的价值就会见失效。Cookie将用户信息保存于客户端,它有自然的安全隐患,一些重中之重的消息不能够积存于Cookie中。一旦客户端禁止使用Cookie,则该功能就是将失去利用的企图。

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的出现解决了上述的烦心,它好以用户的个人化信息保存在指定的数据库中。ASP.NET
2.0的Profile功能默认支持Access数据库暨SQL
Server数据库,如果需要支持任何数据库,可以编写相关的ProfileProvider类。Profile对象是强类型的,我们得以吧用户信息建立属性,以PetShop
4.0吧条例,它白手起家了ShoppingCart、WishList和AccountInfo属性。

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

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

图片 439public interface IPetShopProfileProvider 
图片 440图片 441图片 442
图片 443 AddressInfo GetAccountInfo(string userName, string appName);   
图片 444 void SetAccountInfo(int uniqueID, AddressInfo addressInfo);
图片 445 IList<CartItemInfo> GetCartItems(string userName, string appName, 
图片 446bool isShoppingCart);
图片 447 void SetCartItems(int uniqueID, ICollection<CartItemInfo> cartItems, 
图片 448bool isShoppingCart);
图片 449 void UpdateActivityDates(string userName, bool activityOnly, string appName);
图片 450 int GetUniqueID(string userName, bool isAuthenticated, bool ignoreAuthenticationType,
图片 451 string appName);
图片 452 int CreateProfileForUser(string userName, bool isAuthenticated, string appName);
图片 453 IList<string> GetInactiveProfiles(int authenticationOption, 
图片 454DateTime userInactiveSinceDate, string appName);
图片 455 bool DeleteProfile(string userName, string appName);   
图片 456 IList<CustomProfileInfo> GetProfileInfo(int authenticationOption, 
图片 457string usernameToMatch, DateTime userInactiveSinceDate, string appName, 
图片 458out int totalRecords);
图片 459}

图片 460public interface IPetShopProfileProvider 
图片 461图片 462图片 463
图片 464 AddressInfo GetAccountInfo(string userName, string appName);   
图片 465 void SetAccountInfo(int uniqueID, AddressInfo addressInfo);
图片 466 IList<CartItemInfo> GetCartItems(string userName, string appName, 
图片 467bool isShoppingCart);
图片 468 void SetCartItems(int uniqueID, ICollection<CartItemInfo> cartItems, 
图片 469bool isShoppingCart);
图片 470 void UpdateActivityDates(string userName, bool activityOnly, string appName);
图片 471 int GetUniqueID(string userName, bool isAuthenticated, bool ignoreAuthenticationType,
图片 472 string appName);
图片 473 int CreateProfileForUser(string userName, bool isAuthenticated, string appName);
图片 474 IList<string> GetInactiveProfiles(int authenticationOption, 
图片 475DateTime userInactiveSinceDate, string appName);
图片 476 bool DeleteProfile(string userName, string appName);   
图片 477 IList<CustomProfileInfo> GetProfileInfo(int authenticationOption, 
图片 478string usernameToMatch, DateTime userInactiveSinceDate, string appName, 
图片 479out int totalRecords);
图片 480}

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

为PetShop 4.0本子分别支持SQL
Server和Oracle数据库,因而其分别定义了少于个例外的PetShopProfileProvider类,实现IPetShopProfileProvider接口,并置身两只不等的模块SQLProfileDAL和OracleProfileDAL中。具体的落实请参见PetShop
4.0之源代码。
同等的,PetShop
4.0也Profile引入了工厂模式,定义了模块ProfileDALFActory,工厂类DataAccess的定义如下:

图片 481图片 482public sealed class DataAccess 图片 483{
图片 484
图片 485    private static readonly string profilePath = ConfigurationManager.AppSettings[“ProfileDAL”];
图片 486图片 487    public static PetShop.IProfileDAL.IPetShopProfileProvider CreatePetShopProfileProvider() 图片 488{
图片 489 string className = profilePath + “.PetShopProfileProvider”;
图片 490 return (PetShop.IProfileDAL.IPetShopProfileProvider)Assembly.Load(profilePath).CreateInstance(className);
图片 491    }
图片 492}

图片 493图片 494public sealed class DataAccess 图片 495{
图片 496
图片 497    private static readonly string profilePath = ConfigurationManager.AppSettings[“ProfileDAL”];
图片 498图片 499    public static PetShop.IProfileDAL.IPetShopProfileProvider CreatePetShopProfileProvider() 图片 500{
图片 501 string className = profilePath + “.PetShopProfileProvider”;
图片 502 return (PetShop.IProfileDAL.IPetShopProfileProvider)Assembly.Load(profilePath).CreateInstance(className);
图片 503    }
图片 504}

当工作逻辑层(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();

在作业逻辑层(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配置文件中布局如下的配置节:

概念了PetShop.Profile.PetShopProfileProvider类后,才堪于web.config配置文件被配备如下的配置节:

图片 505<profile automaticSaveEnabled=”false” defaultProvider=”ShoppingCartProvider”>
图片 506 <providers>
图片 507  <add name=”ShoppingCartProvider” connectionStringName=”SQLProfileConnString” type=”PetShop.Profile.PetShopProfileProvider” applicationName=”.NET Pet Shop 4.0″/>
图片 508  <add name=”WishListProvider” connectionStringName=”SQLProfileConnString” type=”PetShop.Profile.PetShopProfileProvider” applicationName=”.NET Pet Shop 4.0″/>
图片 509  <add name=”AccountInfoProvider” connectionStringName=”SQLProfileConnString” type=”PetShop.Profile.PetShopProfileProvider” applicationName=”.NET Pet Shop 4.0″/>
图片 510 </providers>
图片 511 <properties>
图片 512  <add name=”ShoppingCart” type=”PetShop.BLL.Cart” allowAnonymous=”true” provider=”ShoppingCartProvider”/>
图片 513  <add name=”WishList” type=”PetShop.BLL.Cart” allowAnonymous=”true” provider=”WishListProvider”/>
图片 514  <add name=”AccountInfo” type=”PetShop.Model.AddressInfo” allowAnonymous=”false” provider=”AccountInfoProvider”/>
图片 515 </properties>
图片 516</profile>

图片 517<profile automaticSaveEnabled=”false” defaultProvider=”ShoppingCartProvider”>
图片 518 <providers>
图片 519  <add name=”ShoppingCartProvider” connectionStringName=”SQLProfileConnString” type=”PetShop.Profile.PetShopProfileProvider” applicationName=”.NET Pet Shop 4.0″/>
图片 520  <add name=”WishListProvider” connectionStringName=”SQLProfileConnString” type=”PetShop.Profile.PetShopProfileProvider” applicationName=”.NET Pet Shop 4.0″/>
图片 521  <add name=”AccountInfoProvider” connectionStringName=”SQLProfileConnString” type=”PetShop.Profile.PetShopProfileProvider” applicationName=”.NET Pet Shop 4.0″/>
图片 522 </providers>
图片 523 <properties>
图片 524  <add name=”ShoppingCart” type=”PetShop.BLL.Cart” allowAnonymous=”true” provider=”ShoppingCartProvider”/>
图片 525  <add name=”WishList” type=”PetShop.BLL.Cart” allowAnonymous=”true” provider=”WishListProvider”/>
图片 526  <add name=”AccountInfo” type=”PetShop.Model.AddressInfo” allowAnonymous=”false” provider=”AccountInfoProvider”/>
图片 527 </properties>
图片 528</profile>

每当配备文件中,针对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”/>

每当配备文件中,针对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类型对象时获得之。

假定键值为ProfileDAL的价,正是Profile的厂类PetShop.ProfileDALFactory.DataAccess在动反射技术创造IPetShopProfileProvider类型对象时取得之。

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

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

图片 529图片 530public partial class ShoppingCart : System.Web.UI.Page 图片 531{
图片 532
图片 533图片 534    protected void Page_PreInit(object sender, EventArgs e) 图片 535{
图片 536图片 537        if (!IsPostBack) 图片 538{
图片 539            string itemId = Request.QueryString[“addItem”];
图片 540图片 541            if (!string.IsNullOrEmpty(itemId)) 图片 542{
图片 543                Profile.ShoppingCart.Add(itemId);
图片 544                Profile.Save();
图片 545                // Redirect to prevent duplictations in the cart if user hits “Refresh”
图片 546                Response.Redirect(“~/ShoppingCart.aspx”, true);
图片 547            }
图片 548        }
图片 549    }
图片 550}

图片 551图片 552public partial class ShoppingCart : System.Web.UI.Page 图片 553{
图片 554
图片 555图片 556    protected void Page_PreInit(object sender, EventArgs e) 图片 557{
图片 558图片 559        if (!IsPostBack) 图片 560{
图片 561            string itemId = Request.QueryString[“addItem”];
图片 562图片 563            if (!string.IsNullOrEmpty(itemId)) 图片 564{
图片 565                Profile.ShoppingCart.Add(itemId);
图片 566                Profile.Save();
图片 567                // Redirect to prevent duplictations in the cart if user hits “Refresh”
图片 568                Response.Redirect(“~/ShoppingCart.aspx”, true);
图片 569            }
图片 570        }
图片 571    }
图片 572}

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

每当上述的代码中,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特性

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抽象类。

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类的兑现如下:

OracleMembershipProvider类的兑现所有最高的参考价值,如果我们得定义自己之MembershipProvider类,可以参考该类的落实。
实在OracleMemberShip类的落实并无复杂,在此类中,主要是针对性用户以及用户安全要落实相关的表现。由于当父类MembershipProvider中,已经定义了系操作的虚方法,因此我们要发的是再度写这些虚方法。由于与Membership有关的音讯都是储存于数据库中,因而OracleMembershipProvider与SqlMembershipProvider类的要紧区别还是在对数据库的顾。对于SQL
Server而言,我们运用aspnet_regsql工具也Membership建立了连带的数据表以及存储过程。也许是以文化产权的缘故,Microsoft并没吗Oracle数据库提供类似之家伙,因而需要我们自己去创造membership的数据表。此外,由于没有开创Oracle数据库的贮存过程,因而OracleMembershipProvider类中之落实是直调用SQL语句。以CreateUser()方法吗条例,剔除那些乱七八糟的参数判断和安全性判断,SqlMembershipProvider类的兑现如下:

图片 573public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status)
图片 574图片 575图片 576{
图片 577      MembershipUser user1;
图片 578      //前面的代码略;
图片 579      try
图片 580图片 581      图片 582{
图片 583            SqlConnectionHolder holder1 = null;
图片 584            try
图片 585图片 586            图片 587{
图片 588                  holder1 = SqlConnectionHelper.GetConnection(this._sqlConnectionString, true);
图片 589                  this.CheckSchemaVersion(holder1.Connection);
图片 590                  DateTime time1 = this.RoundToSeconds(DateTime.UtcNow);
图片 591                  SqlCommand command1 = new SqlCommand(“dbo.aspnet_Membership_CreateUser”, holder1.Connection);
图片 592                  command1.CommandTimeout = this.CommandTimeout;
图片 593                  command1.CommandType = CommandType.StoredProcedure;
图片 594                  command1.Parameters.Add(this.CreateInputParam(“@ApplicationName”, SqlDbType.NVarChar, this.ApplicationName));
图片 595                  command1.Parameters.Add(this.CreateInputParam(“@UserName”, SqlDbType.NVarChar, username));
图片 596                  command1.Parameters.Add(this.CreateInputParam(“@Password”, SqlDbType.NVarChar, text2));
图片 597                  command1.Parameters.Add(this.CreateInputParam(“@PasswordSalt”, SqlDbType.NVarChar, text1));
图片 598                  command1.Parameters.Add(this.CreateInputParam(“@Email”, SqlDbType.NVarChar, email));
图片 599                  command1.Parameters.Add(this.CreateInputParam(“@PasswordQuestion”, SqlDbType.NVarChar, passwordQuestion));
图片 600                  command1.Parameters.Add(this.CreateInputParam(“@PasswordAnswer”, SqlDbType.NVarChar, text3));
图片 601                  command1.Parameters.Add(this.CreateInputParam(“@IsApproved”, SqlDbType.Bit, isApproved));
图片 602                  command1.Parameters.Add(this.CreateInputParam(“@UniqueEmail”, SqlDbType.Int, this.RequiresUniqueEmail ? 1 : 0));
图片 603                  command1.Parameters.Add(this.CreateInputParam(“@PasswordFormat”, SqlDbType.Int, (int) this.PasswordFormat));
图片 604                  command1.Parameters.Add(this.CreateInputParam(“@CurrentTimeUtc”, SqlDbType.DateTime, time1));
图片 605                  SqlParameter parameter1 = this.CreateInputParam(“@UserId”, SqlDbType.UniqueIdentifier, providerUserKey);
图片 606                  parameter1.Direction = ParameterDirection.InputOutput;
图片 607                  command1.Parameters.Add(parameter1);
图片 608                  parameter1 = new SqlParameter(“@ReturnValue”, SqlDbType.Int);
图片 609                  parameter1.Direction = ParameterDirection.ReturnValue;
图片 610                  command1.Parameters.Add(parameter1);
图片 611                  command1.ExecuteNonQuery();
图片 612                  int num3 = (parameter1.Value != null) ? ((int) parameter1.Value) : -1;
图片 613                  if ((num3 < 0) || (num3 > 11))
图片 614图片 615                  图片 616{
图片 617                        num3 = 11;
图片 618                  }
图片 619                  status = (MembershipCreateStatus) num3;
图片 620                  if (num3 != 0)
图片 621图片 622                  图片 623{
图片 624                        return null;
图片 625                  }
图片 626                  providerUserKey = new Guid(command1.Parameters[“@UserId”].Value.ToString());
图片 627                  time1 = time1.ToLocalTime();
图片 628                  user1 = new MembershipUser(this.Name, username, providerUserKey, email, passwordQuestion, null, isApproved, false, time1, time1, time1, time1, new DateTime(0x6da, 1, 1));
图片 629            }
图片 630            finally
图片 631图片 632            图片 633{
图片 634                  if (holder1 != null)
图片 635图片 636                  图片 637{
图片 638                        holder1.Close();
图片 639                        holder1 = null;
图片 640                  }
图片 641            }
图片 642      }
图片 643      catch
图片 644图片 645      图片 646{
图片 647            throw;
图片 648      }
图片 649      return user1;
图片 650}

图片 651public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status)
图片 652图片 653图片 654{
图片 655      MembershipUser user1;
图片 656      //前面的代码略;
图片 657      try
图片 658图片 659      图片 660{
图片 661            SqlConnectionHolder holder1 = null;
图片 662            try
图片 663图片 664            图片 665{
图片 666                  holder1 = SqlConnectionHelper.GetConnection(this._sqlConnectionString, true);
图片 667                  this.CheckSchemaVersion(holder1.Connection);
图片 668                  DateTime time1 = this.RoundToSeconds(DateTime.UtcNow);
图片 669                  SqlCommand command1 = new SqlCommand(“dbo.aspnet_Membership_CreateUser”, holder1.Connection);
图片 670                  command1.CommandTimeout = this.CommandTimeout;
图片 671                  command1.CommandType = CommandType.StoredProcedure;
图片 672                  command1.Parameters.Add(this.CreateInputParam(“@ApplicationName”, SqlDbType.NVarChar, this.ApplicationName));
图片 673                  command1.Parameters.Add(this.CreateInputParam(“@UserName”, SqlDbType.NVarChar, username));
图片 674                  command1.Parameters.Add(this.CreateInputParam(“@Password”, SqlDbType.NVarChar, text2));
图片 675                  command1.Parameters.Add(this.CreateInputParam(“@PasswordSalt”, SqlDbType.NVarChar, text1));
图片 676                  command1.Parameters.Add(this.CreateInputParam(“@Email”, SqlDbType.NVarChar, email));
图片 677                  command1.Parameters.Add(this.CreateInputParam(“@PasswordQuestion”, SqlDbType.NVarChar, passwordQuestion));
图片 678                  command1.Parameters.Add(this.CreateInputParam(“@PasswordAnswer”, SqlDbType.NVarChar, text3));
图片 679                  command1.Parameters.Add(this.CreateInputParam(“@IsApproved”, SqlDbType.Bit, isApproved));
图片 680                  command1.Parameters.Add(this.CreateInputParam(“@UniqueEmail”, SqlDbType.Int, this.RequiresUniqueEmail ? 1 : 0));
图片 681                  command1.Parameters.Add(this.CreateInputParam(“@PasswordFormat”, SqlDbType.Int, (int) this.PasswordFormat));
图片 682                  command1.Parameters.Add(this.CreateInputParam(“@CurrentTimeUtc”, SqlDbType.DateTime, time1));
图片 683                  SqlParameter parameter1 = this.CreateInputParam(“@UserId”, SqlDbType.UniqueIdentifier, providerUserKey);
图片 684                  parameter1.Direction = ParameterDirection.InputOutput;
图片 685                  command1.Parameters.Add(parameter1);
图片 686                  parameter1 = new SqlParameter(“@ReturnValue”, SqlDbType.Int);
图片 687                  parameter1.Direction = ParameterDirection.ReturnValue;
图片 688                  command1.Parameters.Add(parameter1);
图片 689                  command1.ExecuteNonQuery();
图片 690                  int num3 = (parameter1.Value != null) ? ((int) parameter1.Value) : -1;
图片 691                  if ((num3 < 0) || (num3 > 11))
图片 692图片 693                  图片 694{
图片 695                        num3 = 11;
图片 696                  }
图片 697                  status = (MembershipCreateStatus) num3;
图片 698                  if (num3 != 0)
图片 699图片 700                  图片 701{
图片 702                        return null;
图片 703                  }
图片 704                  providerUserKey = new Guid(command1.Parameters[“@UserId”].Value.ToString());
图片 705                  time1 = time1.ToLocalTime();
图片 706                  user1 = new MembershipUser(this.Name, username, providerUserKey, email, passwordQuestion, null, isApproved, false, time1, time1, time1, time1, new DateTime(0x6da, 1, 1));
图片 707            }
图片 708            finally
图片 709图片 710            图片 711{
图片 712                  if (holder1 != null)
图片 713图片 714                  图片 715{
图片 716                        holder1.Close();
图片 717                        holder1 = null;
图片 718                  }
图片 719            }
图片 720      }
图片 721      catch
图片 722图片 723      图片 724{
图片 725            throw;
图片 726      }
图片 727      return user1;
图片 728}

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

代码中,aspnet_Membership_CreateUser为aspnet_regsql工具为membership创建的积存过程,它的效力就是是创造一个用户。

OracleMembershipProvider类中针对CreateUser()方法的概念如下:

OracleMembershipProvider类中针对CreateUser()方法的概念如下:

图片 729图片 730public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object userId, out MembershipCreateStatus status) 图片 731{
图片 732    //前面的代码略;
图片 733 //Create connection
图片 734 OracleConnection connection = new OracleConnection(OracleHelper.ConnectionStringMembership);
图片 735 connection.Open();
图片 736 OracleTransaction transaction = connection.BeginTransaction(IsolationLevel.ReadCommitted);
图片 737图片 738 try 图片 739{
图片 740  DateTime dt = DateTime.Now;
图片 741  bool isUserNew = true;
图片 742
图片 743  // Step 1: Check if the user exists in the Users table: create if not    
图片 744  int uid = GetUserID(transaction, applicationId, username, true, false, dt, out isUserNew);
图片 745图片 746  if(uid == 0) 图片 747{ // User not created successfully!
图片 748   status = MembershipCreateStatus.ProviderError;
图片 749   return null;
图片 750  }
图片 751  // Step 2: Check if the user exists in the Membership table: Error if yes.
图片 752图片 753  if(IsUserInMembership(transaction, uid)) 图片 754{
图片 755   status = MembershipCreateStatus.DuplicateUserName;
图片 756   return null;
图片 757  }
图片 758  // Step 3: Check if Email is duplicate
图片 759图片 760  if(IsEmailInMembership(transaction, email, applicationId)) 图片 761{
图片 762   status = MembershipCreateStatus.DuplicateEmail;
图片 763   return null;
图片 764  }
图片 765  // Step 4: Create user in Membership table     
图片 766  int pFormat = (int)passwordFormat;
图片 767图片 768  if(!InsertUser(transaction, uid, email, pass, pFormat, salt, “”, “”, isApproved, dt)) 图片 769{
图片 770   status = MembershipCreateStatus.ProviderError;
图片 771   return null;
图片 772  }
图片 773  // Step 5: Update activity date if user is not new
图片 774图片 775  if(!isUserNew) 图片 776{
图片 777图片 778   if(!UpdateLastActivityDate(transaction, uid, dt)) 图片 779{
图片 780    status = MembershipCreateStatus.ProviderError;
图片 781    return null;
图片 782   }
图片 783  }
图片 784  status = MembershipCreateStatus.Success;
图片 785  return new MembershipUser(this.Name, username, uid, email, passwordQuestion, null, isApproved, false, dt, dt, dt, dt, DateTime.MinValue);
图片 786 }
图片 787图片 788 catch(Exception) 图片 789{
图片 790  if(status == MembershipCreateStatus.Success)
图片 791   status = MembershipCreateStatus.ProviderError;
图片 792  throw;
图片 793 }
图片 794图片 795 finally 图片 796{
图片 797  if(status == MembershipCreateStatus.Success)
图片 798   transaction.Commit();
图片 799  else
图片 800   transaction.Rollback();
图片 801  connection.Close();
图片 802  connection.Dispose();
图片 803 }
图片 804}

图片 805图片 806public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object userId, out MembershipCreateStatus status) 图片 807{
图片 808    //前面的代码略;
图片 809 //Create connection
图片 810 OracleConnection connection = new OracleConnection(OracleHelper.ConnectionStringMembership);
图片 811 connection.Open();
图片 812 OracleTransaction transaction = connection.BeginTransaction(IsolationLevel.ReadCommitted);
图片 813图片 814 try 图片 815{
图片 816  DateTime dt = DateTime.Now;
图片 817  bool isUserNew = true;
图片 818
图片 819  // Step 1: Check if the user exists in the Users table: create if not    
图片 820  int uid = GetUserID(transaction, applicationId, username, true, false, dt, out isUserNew);
图片 821图片 822  if(uid == 0) 图片 823{ // User not created successfully!
图片 824   status = MembershipCreateStatus.ProviderError;
图片 825   return null;
图片 826  }
图片 827  // Step 2: Check if the user exists in the Membership table: Error if yes.
图片 828图片 829  if(IsUserInMembership(transaction, uid)) 图片 830{
图片 831   status = MembershipCreateStatus.DuplicateUserName;
图片 832   return null;
图片 833  }
图片 834  // Step 3: Check if Email is duplicate
图片 835图片 836  if(IsEmailInMembership(transaction, email, applicationId)) 图片 837{
图片 838   status = MembershipCreateStatus.DuplicateEmail;
图片 839   return null;
图片 840  }
图片 841  // Step 4: Create user in Membership table     
图片 842  int pFormat = (int)passwordFormat;
图片 843图片 844  if(!InsertUser(transaction, uid, email, pass, pFormat, salt, “”, “”, isApproved, dt)) 图片 845{
图片 846   status = MembershipCreateStatus.ProviderError;
图片 847   return null;
图片 848  }
图片 849  // Step 5: Update activity date if user is not new
图片 850图片 851  if(!isUserNew) 图片 852{
图片 853图片 854   if(!UpdateLastActivityDate(transaction, uid, dt)) 图片 855{
图片 856    status = MembershipCreateStatus.ProviderError;
图片 857    return null;
图片 858   }
图片 859  }
图片 860  status = MembershipCreateStatus.Success;
图片 861  return new MembershipUser(this.Name, username, uid, email, passwordQuestion, null, isApproved, false, dt, dt, dt, dt, DateTime.MinValue);
图片 862 }
图片 863图片 864 catch(Exception) 图片 865{
图片 866  if(status == MembershipCreateStatus.Success)
图片 867   status = MembershipCreateStatus.ProviderError;
图片 868  throw;
图片 869 }
图片 870图片 871 finally 图片 872{
图片 873  if(status == MembershipCreateStatus.Success)
图片 874   transaction.Commit();
图片 875  else
图片 876   transaction.Rollback();
图片 877  connection.Close();
图片 878  connection.Dispose();
图片 879 }
图片 880}

代码中,InsertUser()方法就是是背用户的创始,而于前面虽然用看清创建的用户是否曾经在。InsertUser()方法的概念如下:

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

图片 881图片 882private static bool InsertUser(OracleTransaction transaction, int userId, string email, string password, int passFormat, string passSalt, string passQuestion, string passAnswer, bool isApproved, DateTime dt) 图片 883{
图片 884
图片 885 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)”;
图片 886图片 887 OracleParameter[] insertParms = 图片 888{ 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) };
图片 889 insertParms[0].Value = userId;
图片 890 insertParms[1].Value = email;
图片 891 insertParms[2].Value = password;
图片 892 insertParms[3].Value = passFormat;
图片 893 insertParms[4].Value = passSalt;
图片 894 insertParms[5].Value = passQuestion;
图片 895 insertParms[6].Value = passAnswer;
图片 896 insertParms[7].Value = OracleHelper.OraBit(isApproved);
图片 897 insertParms[8].Value = dt;
图片 898 insertParms[9].Value = dt;
图片 899 insertParms[10].Value = dt;
图片 900
图片 901 if(OracleHelper.ExecuteNonQuery(transaction, CommandType.Text, insert, insertParms) != 1)
图片 902  return false;
图片 903 else
图片 904  return true;
图片 905}

图片 906图片 907private static bool InsertUser(OracleTransaction transaction, int userId, string email, string password, int passFormat, string passSalt, string passQuestion, string passAnswer, bool isApproved, DateTime dt) 图片 908{
图片 909
图片 910 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)”;
图片 911图片 912 OracleParameter[] insertParms = 图片 913{ 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) };
图片 914 insertParms[0].Value = userId;
图片 915 insertParms[1].Value = email;
图片 916 insertParms[2].Value = password;
图片 917 insertParms[3].Value = passFormat;
图片 918 insertParms[4].Value = passSalt;
图片 919 insertParms[5].Value = passQuestion;
图片 920 insertParms[6].Value = passAnswer;
图片 921 insertParms[7].Value = OracleHelper.OraBit(isApproved);
图片 922 insertParms[8].Value = dt;
图片 923 insertParms[9].Value = dt;
图片 924 insertParms[10].Value = dt;
图片 925
图片 926 if(OracleHelper.ExecuteNonQuery(transaction, CommandType.Text, insert, insertParms) != 1)
图片 927  return false;
图片 928 else
图片 929  return true;
图片 930}

当为Membership建立了Provider类后,还需以配备文件被配备相关的配置节,例如SqlMembershipProvider的部署:

以也Membership建立了Provider类后,还用在布置文件中配备相关的配置节,例如SqlMembershipProvider的配置:

图片 931<membership defaultProvider=”SQLMembershipProvider”>
图片 932 <providers>
图片 933  <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”/>
图片 934 </providers>
图片 935</membership>

图片 936<membership defaultProvider=”SQLMembershipProvider”>
图片 937 <providers>
图片 938  <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”/>
图片 939 </providers>
图片 940</membership>

对OracleMembershipProvider而言,配置大致相像:

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

图片 941<membership defaultProvider=”OracleMembershipProvider”>
图片 942 <providers>
图片 943  <clear/>
图片 944  <add name=”OracleMembershipProvider” 
图片 945   type=”PetShop.Membership.OracleMembershipProvider” 
图片 946   connectionStringName=”OraMembershipConnString” 
图片 947   enablePasswordRetrieval=”false” 
图片 948   enablePasswordReset=”false” 
图片 949   requiresUniqueEmail=”false” 
图片 950   requiresQuestionAndAnswer=”false” 
图片 951   minRequiredPasswordLength=”7″ 
图片 952   minRequiredNonalphanumericCharacters=”1″ 
图片 953   applicationName=”.NET Pet Shop 4.0″ 
图片 954   hashAlgorithmType=”SHA1″ 
图片 955   passwordFormat=”Hashed”/>
图片 956 </providers>
图片 957</membership>

图片 958<membership defaultProvider=”OracleMembershipProvider”>
图片 959 <providers>
图片 960  <clear/>
图片 961  <add name=”OracleMembershipProvider” 
图片 962   type=”PetShop.Membership.OracleMembershipProvider” 
图片 963   connectionStringName=”OraMembershipConnString” 
图片 964   enablePasswordRetrieval=”false” 
图片 965   enablePasswordReset=”false” 
图片 966   requiresUniqueEmail=”false” 
图片 967   requiresQuestionAndAnswer=”false” 
图片 968   minRequiredPasswordLength=”7″ 
图片 969   minRequiredNonalphanumericCharacters=”1″ 
图片 970   applicationName=”.NET Pet Shop 4.0″ 
图片 971   hashAlgorithmType=”SHA1″ 
图片 972   passwordFormat=”Hashed”/>
图片 973 </providers>
图片 974</membership>

至于部署节属性的义,可以参照MSDN等息息相关文档。

有关安排节属性的意思,可以参见MSDN等相关文档。

6.4.3  ASP.NET登录控件

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.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等类别的控件,用法如下所示:

图片 975<asp:Login ID=”Login” runat=”server” CreateUserUrl=”~/NewUser.aspx” SkinID=”Login” FailureText=”Login failed. Please try again.”>
图片 976</asp:Login>

图片 977<asp:Login ID=”Login” runat=”server” CreateUserUrl=”~/NewUser.aspx” SkinID=”Login” FailureText=”Login failed. Please try again.”>
图片 978</asp:Login>

又比如说NewUser.aspx中针对CreateUserWizard控件的采用:

再者像NewUser.aspx中对CreateUserWizard控件的用:

图片 979<asp:CreateUserWizard ID=”CreateUserWizard” runat=”server” CreateUserButtonText=”Sign Up” InvalidPasswordErrorMessage=”Please enter a more secure password.” PasswordRegularExpressionErrorMessage=”Please enter a more secure password.” 
图片 980RequireEmail=”False” SkinID=”NewUser”>
图片 981<WizardSteps>
图片 982            <asp:CreateUserWizardStep ID=”CreateUserWizardStep1″ runat=”server”>
图片 983   </asp:CreateUserWizardStp>
图片 984 </WizardSteps>
图片 985</asp:CreateUserWizard>

图片 986<asp:CreateUserWizard ID=”CreateUserWizard” runat=”server” CreateUserButtonText=”Sign Up” InvalidPasswordErrorMessage=”Please enter a more secure password.” PasswordRegularExpressionErrorMessage=”Please enter a more secure password.” 
图片 987RequireEmail=”False” SkinID=”NewUser”>
图片 988<WizardSteps>
图片 989            <asp:CreateUserWizardStep ID=”CreateUserWizardStep1″ runat=”server”>
图片 990   </asp:CreateUserWizardStp>
图片 991 </WizardSteps>
图片 992</asp:CreateUserWizard>

下了登录控件后,我们毋需编写和用户登录相关的代码,登录控件已经也咱得了系的机能,这就算大大地简化了是体系的宏图和贯彻。

利用了登录控件后,我们毋需编写和用户登录相关的代码,登录控件已经也我们得了有关的功用,这就是大妈地简化了这系统的规划以及落实。

6.4.4  Master Page特性

6.4.4  Master Page特性

Master Page相当于是一切Web站点的联合模板,建立之Master
Page文件扩展名为.master。它好分包静态文本、html元素和服务器控件。Master
Page由新鲜之@Master指令识别,如:

Master Page相当于是一切Web站点的统一模板,建立之Master
Page文件扩展名为.master。它可涵盖静态文本、html元素和服务器控件。Master
Page由新鲜的@Master指令识别,如:

图片 993<%@ Master Language=”C#” CodeFile=”MasterPage.master.cs” Inherits=”MasterPage” %>

图片 994<%@ Master Language=”C#” CodeFile=”MasterPage.master.cs” Inherits=”MasterPage” %>

运Master
Page可以呢网站建立一个集合的体,且会以它便宜地创建同组控件和代码,然后以那个用为同一组页。对于那些体制与功能相似之页而言,利用Master
Page就足以集中处理为Master
Page,一旦进行改动,就可当一个职位及进展翻新。

动用Master
Page可以啊网站建立一个集合之体,且会以她好地创造同组控件和代码,然后以那个利用被同一组页。对于那些体制与效能相似之页而言,利用Master
Page就好集中处理为Master
Page,一旦进行改动,就得当一个岗位及拓展创新。

以PetShop 4.0挨,建立了名叫吧MasterPage.master的Master
Page,它蕴含了header、LoginView控件、导航菜单与用于呈现内容之html元素,如图6-3所著: 

当PetShop 4.0受到,建立了名吧MasterPage.master的Master
Page,它包含了header、LoginView控件、导航菜单与用于呈现内容之html元素,如图6-3所展示: 

图片 995

图片 996

图6-3 PetShop 4.0的Master Page

图6-3 PetShop 4.0的Master Page

@Master指令的概念如下:

@Master指令的概念如下:

图片 997<%@ Master Language=”C#” AutoEventWireup=”true” CodeFile=”MasterPage.master.cs” Inherits=”PetShop.Web.MasterPage” %>

图片 998<%@ 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中:

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

图片 999public partial class MasterPage : System.Web.UI.MasterPage {
图片 1000
图片 1001    private const string HEADER_PREFIX = “.NET Pet Shop :: {0}”;
图片 1002
图片 1003    protected void Page_PreRender(object sender, EventArgs e) { 
图片 1004        ltlHeader.Text = Page.Header.Title;
图片 1005        Page.Header.Title = string.Format(HEADER_PREFIX, Page.Header.Title);          
图片 1006    }
图片 1007    protected void btnSearch_Click(object sender, EventArgs e) {
图片 1008        WebUtility.SearchRedirect(txtSearch.Text);    
图片 1009    }
图片 1010}

图片 1011public partial class MasterPage : System.Web.UI.MasterPage {
图片 1012
图片 1013    private const string HEADER_PREFIX = “.NET Pet Shop :: {0}”;
图片 1014
图片 1015    protected void Page_PreRender(object sender, EventArgs e) { 
图片 1016        ltlHeader.Text = Page.Header.Title;
图片 1017        Page.Header.Title = string.Format(HEADER_PREFIX, Page.Header.Title);          
图片 1018    }
图片 1019    protected void btnSearch_Click(object sender, EventArgs e) {
图片 1020        WebUtility.SearchRedirect(txtSearch.Text);    
图片 1021    }
图片 1022}

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

小心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指令的概念如下:

各级一个和Master
Page相关的情页必须于@Page指令的MasterPageFile属性中引用相关的Master
Page。例如PetShop 4.0饱受之CheckOut内容页,其@Page指令的概念如下:

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

图片 1024<%@ 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”%>

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

假使内容页则可根据情况对Parent.master或者Child.master页面。

万一内容页则可以因气象对Parent.master或者Child.master页面。

虽然说Master
Page大部分情形下是盖宣称方式创造,但咱啊可建立一个类继承System.Web.UI.MasterPage,从而完成对Master
Page的编程式创建。但当运这种艺术的还要,应该同时创建.master文件。此外对Master
Page的调用也堪行使编程的方法成功,例如动态地添加Master
Page,我们又写内容页的Page_PreInit()方法,如下所示:

虽说Master
Page大部分动静下是为宣称方式创造,但我们也得以起一个类继承System.Web.UI.MasterPage,从而成就对Master
Page的编程式创建。但每当采取这种方法的而,应该同时创建.master文件。此外针对Master
Page的调用也可以应用编程的办法成就,例如动态地添加Master
Page,我们又写内容页的Page_PreInit()方法,如下所示:

图片 1025void Page_PreInit(Object sender, EventArgs e)
图片 1026图片 1027图片 1028{
图片 1029    this.MasterPageFile = “~/NewMaster.master”;
图片 1030}

图片 1031void Page_PreInit(Object sender, EventArgs e)
图片 1032图片 1033图片 1034{
图片 1035    this.MasterPageFile = “~/NewMaster.master”;
图片 1036}

因此重新写Page_PreInit()方法,是因Master
Page会在内容页初始化阶段展开联合,也就凡说凡是于PreInit阶段完成Master
Page的分配。
ASP.NET
2.0引入的初特点,并不只限于上述介绍的情节。例如Theme、Wizard控件等新特色在PetShop
4.0遭受吗获取了大量的应用。虽然ASP.NET
2.0即时地吐故纳新,对表示层的计划性有改善,然而作为ASP.NET
2.0的里边一部分,它们只是是针对现有框架缺失的弥补与改进,属于“锦上添花”的层面,对于一切表示层设计技术而言,起至的推进作用也百般少。

用重新写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(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,可以以代表层设计达到带来重新多之喜怒哀乐。

自己本尚无法预知AJAX技术于未来底走向,然而仅从象征层设计之角度而言,AJAX技术一样带了同样街全新的革命。我们要可以期待未来之PetShop
5.0,可以于表示层设计及带来双重多的大悲大喜。