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:
publicinterfaceIPetShopProfileProvider
{
AddressInfoGetAccountInfo(stringuserName,stringappName);
voidSetAccountInfo(intuniqueID,AddressInfoaddressInfo);
IList<CartItemInfo>GetCartItems(stringuserName,stringappName,
boolisShoppingCart);
voidSetCartItems(intuniqueID,ICollection<CartItemInfo>cartItems,
boolisShoppingCart);
voidUpdateActivityDates(stringuserName,boolactivityOnly,stringappName);
intGetUniqueID(stringuserName,boolisAuthenticated,boolignoreAuthenticationType,
stringappName);
intCreateProfileForUser(stringuserName,boolisAuthenticated,stringappName);
IList<string>GetInactiveProfiles(intauthenticationOption,
DateTimeuserInactiveSinceDate,stringappName);
boolDeleteProfile(stringuserName,stringappName);
IList<CustomProfileInfo>GetProfileInfo(intauthenticationOption,
stringusernameToMatch,DateTimeuserInactiveSinceDate,stringappName,
outinttotalRecords);
}
因为PetShop 4.0版本分别支持SQL Server和Oracle数据库,因而它分别定义了两个不同的PetShopProfileProvider类,实现IPetShopProfileProvider接口,并放在两个不同的模块SQLProfileDAL和OracleProfileDAL中。具体的实现请参见PetShop 4.0的源代码。
同样的,PetShop 4.0为Profile引入了工厂模式,定义了模块ProfileDALFActory,工厂类DataAccess的定义如下:
publicsealedclassDataAccess{
privatestaticreadonlystringprofilePath=ConfigurationManager.AppSettings["ProfileDAL"];
publicstaticPetShop.IProfileDAL.IPetShopProfileProviderCreatePetShopProfileProvider(){
stringclassName=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配置文件中配置如下的配置节:
<profileautomaticSaveEnabled="false"defaultProvider="ShoppingCartProvider">
<providers>
<addname="ShoppingCartProvider"connectionStringName="SQLProfileConnString"type="PetShop.Profile.PetShopProfileProvider"applicationName=".NETPetShop4.0"/>
<addname="WishListProvider"connectionStringName="SQLProfileConnString"type="PetShop.Profile.PetShopProfileProvider"applicationName=".NETPetShop4.0"/>
<addname="AccountInfoProvider"connectionStringName="SQLProfileConnString"type="PetShop.Profile.PetShopProfileProvider"applicationName=".NETPetShop4.0"/>
</providers>
<properties>
<addname="ShoppingCart"type="PetShop.BLL.Cart"allowAnonymous="true"provider="ShoppingCartProvider"/>
<addname="WishList"type="PetShop.BLL.Cart"allowAnonymous="true"provider="WishListProvider"/>
<addname="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属性:
publicpartialclassShoppingCart:System.Web.UI.Page{
protectedvoidPage_PreInit(objectsender,EventArgse){
if(!IsPostBack){
stringitemId=Request.QueryString["addItem"];
if(!string.IsNullOrEmpty(itemId)){
Profile.ShoppingCart.Add(itemId);
Profile.Save();
//Redirecttopreventduplictationsinthecartifuserhits"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类的实现如下:
publicoverrideMembershipUserCreateUser(stringusername,stringpassword,stringemail,stringpasswordQuestion,stringpasswordAnswer,boolisApproved,objectproviderUserKey,outMembershipCreateStatusstatus)
{
MembershipUseruser1;
//前面的代码略;
try
{
SqlConnectionHolderholder1=null;
try
{
holder1=SqlConnectionHelper.GetConnection(this._sqlConnectionString,true);
this.CheckSchemaVersion(holder1.Connection);
DateTimetime1=this.RoundToSeconds(DateTime.UtcNow);
SqlCommandcommand1=newSqlCommand("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));
SqlParameterparameter1=this.CreateInputParam("@UserId",SqlDbType.UniqueIdentifier,providerUserKey);
parameter1.Direction=ParameterDirection.InputOutput;
command1.Parameters.Add(parameter1);
parameter1=newSqlParameter("@ReturnValue",SqlDbType.Int);
parameter1.Direction=ParameterDirection.ReturnValue;
command1.Parameters.Add(parameter1);
command1.ExecuteNonQuery();
intnum3=(parameter1.Value!=null)?((int)parameter1.Value):-1;
if((num3<0)||(num3>11))
{
num3=11;
}
status=(MembershipCreateStatus)num3;
if(num3!=0)
{
returnnull;
}
providerUserKey=newGuid(command1.Parameters["@UserId"].Value.ToString());
time1=time1.ToLocalTime();
user1=newMembershipUser(this.Name,username,providerUserKey,email,passwordQuestion,null,isApproved,false,time1,time1,time1,time1,newDateTime(0x6da,1,1));
}
finally
{
if(holder1!=null)
{
holder1.Close();
holder1=null;
}
}
}
catch
{
throw;
}
returnuser1;
}
代码中,aspnet_Membership_CreateUser为aspnet_regsql工具为membership创建的存储过程,它的功能就是创建一个用户。
OracleMembershipProvider类中对CreateUser()方法的定义如下:
publicoverrideMembershipUserCreateUser(stringusername,stringpassword,stringemail,stringpasswordQuestion,stringpasswordAnswer,boolisApproved,objectuserId,outMembershipCreateStatusstatus){
//前面的代码略;
//Createconnection
OracleConnectionconnection=newOracleConnection(OracleHelper.ConnectionStringMembership);
connection.Open();
OracleTransactiontransaction=connection.BeginTransaction(IsolationLevel.ReadCommitted);
try{
DateTimedt=DateTime.Now;
boolisUserNew=true;
//Step1:CheckiftheuserexistsintheUserstable:createifnot
intuid=GetUserID(transaction,applicationId,username,true,false,dt,outisUserNew);
if(uid==0){//Usernotcreatedsuccessfully!
status=MembershipCreateStatus.ProviderError;
returnnull;
}
//Step2:CheckiftheuserexistsintheMembershiptable:Errorifyes.
if(IsUserInMembership(transaction,uid)){
status=MembershipCreateStatus.DuplicateUserName;
returnnull;
}
//Step3:CheckifEmailisduplicate
if(IsEmailInMembership(transaction,email,applicationId)){
status=MembershipCreateStatus.DuplicateEmail;
returnnull;
}
//Step4:CreateuserinMembershiptable
intpFormat=(int)passwordFormat;
if(!InsertUser(transaction,uid,email,pass,pFormat,salt,"","",isApproved,dt)){
status=MembershipCreateStatus.ProviderError;
returnnull;
}
//Step5:Updateactivitydateifuserisnotnew
if(!isUserNew){
if(!UpdateLastActivityDate(transaction,uid,dt)){
status=MembershipCreateStatus.ProviderError;
returnnull;
}
}
status=MembershipCreateStatus.Success;
returnnewMembershipUser(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();
}
}
分享到:
相关推荐
项目名称及描述(这是我从... petshop4.0 详解之五(PetShop之业务逻辑层设计) petshop4.0 详解之六(PetShop表示层设计) petshop4.0 详解之七(PetShop表示层设计) petshop4.0 详解之八(PetShop表示层设计)
Petshop4.0详解 pdf。
petshop4.0详解 petshop4.0详解petshop4.0详解 petshop4.0详解petshop4.0详解petshop4.0详解
petshop 4.0 详解
petshop4.0 详解之五(PetShop之业务逻辑层设计)
PetShop 4.0 详解之五(PetShop之业务逻辑层设计)
petshop4.0详解
PetShop4.0详解(系统架构设计).doc
petshop4.0以及petshop4.0详解。。。。。。。。。。
petshop4.0 详解之四(PetShop之ASP.NET缓存) .doc
petshop4.0 详解petshop4.0 详解petshop4.0 详解petshop4.0 详解petshop4.0 详解petshop4.0 详解petshop4.0 详解petshop4.0 详解
petshop4.0 详解完整版(一至八) .NET 2.0 真正的经典!
微软 petshop 4.0 详解 三层架构 asp.net C#
PetShop4.0架构设计PetShop4.0架构设计PetShop4.0架构设计
PetShop4.0 详解.pdf 采用3层架构的思想~~~ 分别从每一层分析petshop4.0~~~ 是学习petshop思想的新手的不错的选择~~~
PetShop 4.0 官方详解
petshop4.0详解.doc 经典三层架构 学习实例
然而PetShop随着版本的不断更新,至现在基 于.Net 2.0的PetShop4.0为止,整个设计逐渐变得成熟而优雅,却又很多可以借鉴之处。PetShop是一个小型的项目,系统架构与代码都比较简单,却 也凸现了许多颇有价值的设计与...
微软开源项目petshop4.0详解(PDF文档)
PetShop4.0详解.doc