MySQL 數據類型和建庫戰略。本站提示廣大學習愛好者:(MySQL 數據類型和建庫戰略)文章只能為提供參考,不一定能成為您想要的結果。以下是MySQL 數據類型和建庫戰略正文
六 PetShop之表現層設計
表現層(Presentation Layer)的設計可以給體系客戶最直接的體驗和最實足的信念。正如人與人的訂交了解一樣,首次會晤的感到老是永難忘記的。一件交付給客戶應用的產物,假如在用戶界面(User Interface,UI)上缺少吸惹人的特點,界面不友愛,操作不敷體諒,即便這件產物機能異常優良,架構設計公道,營業邏輯都知足了客戶的需求,卻依然難以討得客戶的歡心。鄙諺雲:“佛要金裝,人要衣裝”,特殊是關於Web運用法式而言,Web網頁就比如人的衣裝,代表著全部體系的身份與臉面,是兜攬“顧客”的最年夜賣點。
“獻丑不如藏拙”,作為藝術細胞缺少的我,其實不盤算在用戶界面的美術設計上年夜做文章,是以本書略過不提。本章所存眷的表現層設計,照樣以架構設計的角度,論述在表現層設計中對形式的運用,ASP.NET控件的設計與應用,同時還包含了對ASP.NET 2.0新特點的引見。
6.1 MVC形式
表現層設計中最主要的形式是MVC(Model-View-Controller,即模子-視圖-掌握器)形式。MVC形式最早是由SmallTalk說話研討團提出的,被普遍運用在用戶交互運用法式中。Controller依據用戶要求(Response)修正Model的屬性,此時Event(事宜)被觸發,一切依附於Model的View對象會主動更新,並基於Model對象發生一個呼應(Response)信息,前往給Controller。Martin Fowler在《企業運用架構形式》一書中,展現了MVC形式運用的全進程,如圖6-1所示:
圖6-1 典范的MVC形式
假如將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控件的數據源綁定。
6.2 Page Controller形式的運用
通不雅PetShop的表現層設計,充足應用了ASP.NET的技巧特色,經由過程Web頁面與用戶控件掌握和展示視圖,並應用codebehind技巧將營業邏輯層的范疇對象參加到表現層完成邏輯中,一個典范的Page Controller形式呼之欲出。
Page Controller形式是Martin Fowler在《企業運用架構形式》中最主要的表現層形式之一。在.NET平台下,Page Controller形式的完成異常簡略,以Products.aspx頁面為例。起首在aspx頁面中,停止以下的設置:
<%@ 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要求的接口,供給了以下的界說:
[AspNetHostingPermission(SecurityAction.InheritanceDemand, Level=AspNetHostingPermissionLevel.Minimal), AspNetHostingPermission(SecurityAction.LinkDemand, Level=AspNetHostingPermissionLevel.Minimal)] public interface IHttpHandler { void ProcessRequest(HttpContext context); bool IsReusable { get; } }
Page類完成了ProcessRequest()辦法,經由過程它可以設置Page對象的Request和Response屬性,從而完成對用戶要求/響應的掌握。然後Page類經由過程從Control類繼續來的Load事宜,將View與Model樹立聯系關系,如Products.aspx.cs所示:
public partial class Products : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { //get page header and title Page.Title = WebUtility.GetCategoryName(Request.QueryString["categoryId"]); } }
事宜機制正好是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()辦法,以下所示:
public partial class CheckOut : System.Web.UI.Page protected void wzdCheckOut_FinishButtonClick(object sender, WizardNavigationEventArgs e) { if (Profile.ShoppingCart.CartItems.Count > 0) { if (Profile.ShoppingCart.Count > 0) { // display ordered items CartListOrdered.Bind(Profile.ShoppingCart.CartItems); // display total and credit card information ltlTotalComplete.Text = ltlTotal.Text; ltlCreditCardComplete.Text = ltlCreditCard.Text; // create order OrderInfo order = new OrderInfo(int.MinValue, DateTime.Now, User.Identity.Name, GetCreditCardInfo(), billingForm.Address, shippingForm.Address, Profile.ShoppingCart.Total, Profile.ShoppingCart.GetOrderLineItems(), null); // insert Order newOrder = new Order(); newOrder.Insert(order); // destroy cart Profile.ShoppingCart.Clear(); Profile.Save(); } } else { 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>"; wzdCheckOut.Visible = false; } }
在下面的一段代碼中,異常典范地表達了Model與View之間的關系。它經由過程獲得控件的屬性值,作為參數值傳遞給數據值對象OrderInfo,從而應用頁面上發生的定單信息創立定單對象,然後再挪用范疇對象Order的Inser()辦法將OrderInfo對象拔出到數據表中。另外,它還對范疇對象ShoppingCart的數據項作出斷定,假如其值等於0,就在頁面中顯示UI提醒信息。此時,View的內容決議了Model的值,而Model值反過去又決議了View的顯示內容。
6.3 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等屬性。
除此以外,還有一個主要的類是System.Web.UI.UserControl,即用戶控件類,它異樣是Control類的子類。我們可以自界說一些用戶控件派生自UserControl,在Visual Studio的Design情況下,我們可以經由過程拖動控件的方法將多品種型的控件組分解一個自界說用戶控件,也能夠在codebehind方法下,為自界說用戶控件類添加新的屬性和辦法。
全部ASP.NET控件類的條理構造如圖6-2所示:
圖6-2 ASP.NET控件類的條理構造
ASP.NET控件的履行性命周期如表6-1所示:
階段
表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。
因為這兩個控件都轉變了其父類控件的出現方法,故而,我們可以經由過程重寫父類的Render虛辦法,完成控件的自界說。例如CustomGrid控件:
public class CustomGrid : Repeater… //Static constants protected const string HTML1 = "<table cellpadding=0 cellspacing=0><tr><td colspan=2>"; protected const string HTML2 = "</td></tr><tr><td class=paging align=left>"; protected const string HTML3 = "</td><td align=right class=paging>"; protected const string HTML4 = "</td></tr></table>"; private static readonly Regex RX = new Regex(@"^&page=\d+", RegexOptions.Compiled); private const string LINK_PREV = "<a href=?page={0}>< Previous</a>"; private const string LINK_MORE = "<a href=?page={0}>More ></a>"; private const string KEY_PAGE = "page"; private const string COMMA = "?"; private const string AMP = "&"; override protected void Render(HtmlTextWriter writer) { //Check there is some data attached if (ItemCount == 0) { writer.Write(emptyText); return; } //Mask the query string query = Context.Request.Url.Query.WordStr(COMMA, AMP); query = RX.WordStr(query, string.Empty); // Write out the first part of the control, the table header writer.Write(HTML1); // Call the inherited method base.Render(writer); // Write out a table row closure writer.Write(HTML2); //Determin whether next and previous buttons are required //Previous button? if (currentPageIndex > 0) writer.Write(string.Format(LINK_PREV, (currentPageIndex - 1) + query)); //Close the table data tag writer.Write(HTML3); //Next button? if (currentPageIndex < PageCount) writer.Write(string.Format(LINK_MORE, (currentPageIndex + 1) + query)); //Close the table writer.Write(HTML4); }
因為CustomGrid繼續自Repeater控件,因此它同時還繼續了Repeater的DataSource屬性,這是一個虛屬性,它默許的set拜訪器屬性以下:
public virtual object DataSource { get {… } set { if (((value != null) && !(value is IListSource)) && !(value is IEnumerable)) { throw new ArgumentException(SR.GetString("Invalid_DataSource_Type", new object[] { this.ID })); } this.dataSource = value; this.OnDataPropertyChanged(); } }
關於CustomGrid而言,DataSource屬性有著分歧的設置行動,因此在界說CustomGrid控件的時刻,須要改寫DataSource虛屬性,以下所示:
private IList dataSource; private int itemCount; override public object DataSource { set { //This try catch block is to avoid issues with the VS.NET designer //The designer will try and bind a datasource which does not derive from ILIST try { dataSource = (IList)value; ItemCount = dataSource.Count; } catch { dataSource = null; ItemCount = 0; } } }
當設置的value對象值不為IList類型時,set拜訪器就將捕捉異常,然後將dataSource字段設置為null。
因為我們改寫了DataSource屬性,因此改寫Repeater類的OnDataBinding()辦法也就勢在必行。另外,CustomGrid還供給了分頁的功效,我們也須要完成分頁的相干操作。與DataSource屬性分歧,Repeater類的OnDataBinding()辦法現實上是繼續和改寫了Control基類的OnDataBinding()虛辦法,而我們又在此基本上改寫了Repeater類的OnDataBinding()辦法:
override protected void OnDataBinding(EventArgs e) { //Work out which items we want to render to the page int start = CurrentPageIndex * pageSize; int size = Math.Min(pageSize, ItemCount - start); IList page = new ArrayList(); //Add the relevant items from the datasource for (int i = 0; i < size; i++) page.Add(dataSource[start + i]); //set the base objects datasource base.DataSource = page; base.OnDataBinding(e); }
另外,CustomGrid控件類還增長了很多屬於本身的屬性和辦法,例如PageSize、PageCount屬性和SetPage()辦法等。恰是由於ASP.NET控件引入了Composite形式與Template Method形式,當我們在自界說控件時,便可以經由過程繼續與改寫的方法來完成控件的設計。自界說ASP.NET控件一方面可以依據體系的需務實現特定的功效,也可以或許最年夜限制地完成對象的重用,既可以削減編碼量,同時也有益於將來對法式的擴大與修正。
在PetShop 4.0中,除自界說了上述WebControl控件的子控件外,最重要的照樣應用了用戶控件。在Controls文件夾下,一共界說了11個用戶控件,內容涵蓋客戶地址信息、信譽卡信息、購物車信息、希冀列表(Wish List)信息和導航信息、搜刮成果信息等。它們相當因而一些組合控件,除包括了子控件的辦法和屬性外,也界說了一些需要的UI完成邏輯。以ShoppingCartControl用戶控件為例,它會在該控件被出現(Render)之前,做一些數據預備任務,獲得購物車數據,並作為數據源綁定到其下的Repeater控件:
public partial class ShoppingCartControl : System.Web.UI.UserControl protected void Page_PreRender(object sender, EventArgs e) { if (!IsPostBack) { BindCart(); } } private void BindCart() { ICollection<CartItemInfo> cart = Profile.ShoppingCart.CartItems; if (cart.Count > 0) { repShoppingCart.DataSource = cart; repShoppingCart.DataBind(); PrintTotal(); plhTotal.Visible = true; } else { repShoppingCart.Visible = false; plhTotal.Visible = false; lblMsg.Text = "Your cart is empty."; } }
在ShoppingCart頁面下,我們可以參加該用戶控件,以下所示:
<PetShopControl:shoppingcartcontrol id="ShoppingCartControl1" runat="server"></PetShopControl:shoppingcartcontrol>
因為ShoppingCartControl用戶控件曾經完成了用於出現購物車數據的邏輯,那末在ShoppingCart.aspx.cs中,便可以不消擔任這些邏輯,在充足完成對象重用的進程中,同時又到達了職責分別的目標。用戶控件的設計者與頁面設計者可以互不攪擾,分頭完成本身的設計。特殊是關於頁面設計者而言,他可所以單一的UI設計人員腳色,僅須要存眷用戶界面能否雅觀與友愛,關於表現層中對范疇對象的挪用與操作便可以不用理睬,全部頁面的代碼也顯得構造清楚、邏輯清晰,無疑也“清潔”了很多。
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:
public interface IPetShopProfileProvider { AddressInfo GetAccountInfo(string userName, string appName); void SetAccountInfo(int uniqueID, AddressInfo addressInfo); IList<CartItemInfo> GetCartItems(string userName, string appName, bool isShoppingCart); void SetCartItems(int uniqueID, ICollection<CartItemInfo> cartItems, bool isShoppingCart); void UpdateActivityDates(string userName, bool activityOnly, string appName); int GetUniqueID(string userName, bool isAuthenticated, bool ignoreAuthenticationType, string appName); int CreateProfileForUser(string userName, bool isAuthenticated, string appName); IList<string> GetInactiveProfiles(int authenticationOption, DateTime userInactiveSinceDate, string appName); bool DeleteProfile(string userName, string appName); IList<CustomProfileInfo> GetProfileInfo(int authenticationOption, string usernameToMatch, DateTime userInactiveSinceDate, string appName, out int totalRecords); }
由於PetShop 4.0版天職別支撐SQL Server和Oracle數據庫,因此它分離界說了兩個分歧的PetShopProfileProvider類,完成IPetShopProfileProvider接口,並放在兩個分歧的模塊SQLProfileDAL和OracleProfileDAL中。詳細的完成請拜見PetShop 4.0的源代碼。
異樣的,PetShop 4.0為Profile引入了工場形式,界說了模塊ProfileDALFActory,工場類DataAccess的界說以下:
public sealed class DataAccess { private static readonly string profilePath = ConfigurationManager.AppSettings["ProfileDAL"]; public static PetShop.IProfileDAL.IPetShopProfileProvider CreatePetShopProfileProvider() { string className = profilePath + ".PetShopProfileProvider"; return (PetShop.IProfileDAL.IPetShopProfileProvider)Assembly.Load(profilePath).CreateInstance(className); } }
在營業邏輯層(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設置裝備擺設文件中設置裝備擺設以下的設置裝備擺設節:
<profile automaticSaveEnabled="false" defaultProvider="ShoppingCartProvider"> <providers> <add name="ShoppingCartProvider" connectionStringName="SQLProfileConnString" type="PetShop.Profile.PetShopProfileProvider" applicationName=".NET Pet Shop 4.0"/> <add name="WishListProvider" connectionStringName="SQLProfileConnString" type="PetShop.Profile.PetShopProfileProvider" applicationName=".NET Pet Shop 4.0"/> <add name="AccountInfoProvider" connectionStringName="SQLProfileConnString" type="PetShop.Profile.PetShopProfileProvider" applicationName=".NET Pet Shop 4.0"/> </providers> <properties> <add name="ShoppingCart" type="PetShop.BLL.Cart" allowAnonymous="true" provider="ShoppingCartProvider"/> <add name="WishList" type="PetShop.BLL.Cart" allowAnonymous="true" provider="WishListProvider"/> <add name="AccountInfo" type="PetShop.Model.AddressInfo" allowAnonymous="false" provider="AccountInfoProvider"/> </properties> </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"/>
而鍵值為ProfileDAL的值,恰是Profile的工場類PetShop.ProfileDALFactory.DataAccess在應用反射技巧創立IPetShopProfileProvider類型對象時獲得的。
在表現層中,可以應用頁面的Profile屬性拜訪用戶的特性化屬性,例如在ShoppingCart頁面的codebehind代碼ShoppingCart.aspx.cs中,挪用Profile的ShoppingCart屬性:
public partial class ShoppingCart : System.Web.UI.Page { protected void Page_PreInit(object sender, EventArgs e) { if (!IsPostBack) { string itemId = Request.QueryString["addItem"]; if (!string.IsNullOrEmpty(itemId)) { Profile.ShoppingCart.Add(itemId); Profile.Save(); // Redirect to prevent duplictations in the cart if user hits "Refresh" Response.Redirect("~/ShoppingCart.aspx", true); } } } }
在上述的代碼中,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類的完成以下:
public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status) { MembershipUser user1; //後面的代碼略; try { SqlConnectionHolder holder1 = null; try { holder1 = SqlConnectionHelper.GetConnection(this._sqlConnectionString, true); this.CheckSchemaVersion(holder1.Connection); DateTime time1 = this.RoundToSeconds(DateTime.UtcNow); SqlCommand command1 = new SqlCommand("dbo.aspnet_Membership_CreateUser", holder1.Connection); command1.CommandTimeout = this.CommandTimeout; command1.CommandType = CommandType.StoredProcedure; command1.Parameters.Add(this.CreateInputParam("@ApplicationName", SqlDbType.NVarChar, this.ApplicationName)); command1.Parameters.Add(this.CreateInputParam("@UserName", SqlDbType.NVarChar, username)); command1.Parameters.Add(this.CreateInputParam("@Password", SqlDbType.NVarChar, text2)); command1.Parameters.Add(this.CreateInputParam("@PasswordSalt", SqlDbType.NVarChar, text1)); command1.Parameters.Add(this.CreateInputParam("@Email", SqlDbType.NVarChar, email)); command1.Parameters.Add(this.CreateInputParam("@PasswordQuestion", SqlDbType.NVarChar, passwordQuestion)); command1.Parameters.Add(this.CreateInputParam("@PasswordAnswer", SqlDbType.NVarChar, text3)); command1.Parameters.Add(this.CreateInputParam("@IsApproved", SqlDbType.Bit, isApproved)); command1.Parameters.Add(this.CreateInputParam("@UniqueEmail", SqlDbType.Int, this.RequiresUniqueEmail ? 1 : 0)); command1.Parameters.Add(this.CreateInputParam("@PasswordFormat", SqlDbType.Int, (int) this.PasswordFormat)); command1.Parameters.Add(this.CreateInputParam("@CurrentTimeUtc", SqlDbType.DateTime, time1)); SqlParameter parameter1 = this.CreateInputParam("@UserId", SqlDbType.UniqueIdentifier, providerUserKey); parameter1.Direction = ParameterDirection.InputOutput; command1.Parameters.Add(parameter1); parameter1 = new SqlParameter("@ReturnValue", SqlDbType.Int); parameter1.Direction = ParameterDirection.ReturnValue; command1.Parameters.Add(parameter1); command1.ExecuteNonQuery(); int num3 = (parameter1.Value != null) ? ((int) parameter1.Value) : -1; if ((num3 < 0) || (num3 > 11)) { num3 = 11; } status = (MembershipCreateStatus) num3; if (num3 != 0) { return null; } providerUserKey = new Guid(command1.Parameters["@UserId"].Value.ToString()); time1 = time1.ToLocalTime(); user1 = new MembershipUser(this.Name, username, providerUserKey, email, passwordQuestion, null, isApproved, false, time1, time1, time1, time1, new DateTime(0x6da, 1, 1)); } finally { if (holder1 != null) { holder1.Close(); holder1 = null; } } } catch { throw; } return user1; }
代碼中,aspnet_Membership_CreateUser為aspnet_regsql對象為membership創立的存儲進程,它的功效就是創立一個用戶。
OracleMembershipProvider類中對CreateUser()辦法的界說以下:
public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object userId, out MembershipCreateStatus status) { //後面的代碼略; //Create connection OracleConnection connection = new OracleConnection(OracleHelper.ConnectionStringMembership); connection.Open(); OracleTransaction transaction = connection.BeginTransaction(IsolationLevel.ReadCommitted); try { DateTime dt = DateTime.Now; bool isUserNew = true; // Step 1: Check if the user exists in the Users table: create if not int uid = GetUserID(transaction, applicationId, username, true, false, dt, out isUserNew); if(uid == 0) { // User not created successfully! status = MembershipCreateStatus.ProviderError; return null; } // Step 2: Check if the user exists in the Membership table: Error if yes. if(IsUserInMembership(transaction, uid)) { status = MembershipCreateStatus.DuplicateUserName; return null; } // Step 3: Check if Email is duplicate if(IsEmailInMembership(transaction, email, applicationId)) { status = MembershipCreateStatus.DuplicateEmail; return null; } // Step 4: Create user in Membership table int pFormat = (int)passwordFormat; if(!InsertUser(transaction, uid, email, pass, pFormat, salt, "", "", isApproved, dt)) { status = MembershipCreateStatus.ProviderError; return null; } // Step 5: Update activity date if user is not new if(!isUserNew) { if(!UpdateLastActivityDate(transaction, uid, dt)) { status = MembershipCreateStatus.ProviderError; return null; } } status = MembershipCreateStatus.Success; return new MembershipUser(this.Name, username, uid, email, passwordQuestion, null, isApproved, false, dt, dt, dt, dt, DateTime.MinValue); } catch(Exception) { if(status == MembershipCreateStatus.Success) status = MembershipCreateStatus.ProviderError; throw; } finally { if(status == MembershipCreateStatus.Success) transaction.Commit(); else transaction.Rollback(); connection.Close(); connection.Dispose(); } }
代碼中,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 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,可以在表現層設計上帶來更多的欣喜。
以上就是PetShop的表現層設計全體內容,願望能給年夜家一個參考,也願望年夜家多多支撐。