Community Server专题九:MemberRole之Profile

上个专题我们讲到了Membership,了解了Membership可以让我们轻松的实现用户注册、登录、删除用户、用户更改密码等等一系列关于用户的基本操作,但是对于一个面向多用户的web程序,为了满足用户在访问站点时能够根据自己的喜好定制web站点的一些页面布局、皮肤,语言等等一些信息,Membership是不能满足要求的,因此,在CS中引入了Profile,Profile并不是为了实现用户个性化的机制,只是实现了个性化信息存储(在asp.net 2.0 beat2.0中页面的个性化可以用webpart实现的),目前CS的版本中还没有实现这个功能。但是我想,CS实现页面布局的个性化是迟早的事情,如果有可能,我会在CS专题结束后做一些asp.net 2.0 webpart的讲解。
CS中的Profile存储的主要是注册用户的email、timezone(时区)、日期格式、字体大小等等。通过这些信息CS就可以给注册并且登录后的用户个性化他们设置的页面,如:选择的皮肤、语言、字体大小,日期格式。
以显示日期格式为例,我们看看Profile给我们带来的效果:
我们先在CS中设置日期格式如下:
可以看到CS论坛中的日期格式如下:
接下来,设置过另外一个格式:
在论坛中可以看到格式也改变了:
如果你注销后,日期格式又会发生改变(改变为默认日期格式),也就是说这种设置完全是个人的。
当然,要实现这样的功能需要构建复杂的代码,Profile机制在这里仅仅提供数据的存储服务,这个专题我们只分析Profile机制是如何提供存储服务的。
先看看配置文件:
节点下的内容我就不多说了。<properties>节点是配置的关键,该节点下面的信息就是一个注册用户可以存储的个性化信息。name是存储的名称,type是该名称保存数据的类型(其实还有一些信息可有可无,比如defaultValue等,表示默认值)。
< profile enabled ="true" >

< providers >

< add

name ="CommunityServerSqlProvider"

type
="CommunityServer.Components.CSProfileProvider,CommunityServer.Components"

connectionStringName
="SiteSqlServer"

applicationName
="dev"

description
="StoresandretrievesprofiledatafromthelocalMicrosoftSQLServerdatabase"

/>

</ providers >



< properties >

< add name ="commonName" type ="string" />

< add name ="birthdate" type ="DateTime" />

< add name ="gender" type ="int" defaultValue ="0" />

< add name ="dateFormat" type ="string" defaultValue ="yyyy-MM-dd" />

< add name ="publicEmail" type ="string" />

< add name ="language" type ="string" />

< add name ="webAddress" type ="string" />

< add name ="webLog" type ="string" />

< add name ="signature" type ="string" />

< add name ="signatureFormatted" type ="string" />

< add name ="location" type ="string" />

< add name ="occupation" type ="string" />

< add name ="interests" type ="string" />

< add name ="msnIM" type ="string" />

< add name ="yahooIM" type ="string" />

< add name ="aolIM" type ="string" />

< add name ="icqIM" type ="string" />

< add name ="qqIM" type ="string" />

< add name ="enablePostPreviewPopup" type ="System.Boolean" defaultValue ="false" />

< add name ="enableEmoticons" type ="System.Boolean" defaultValue ="true" />

< add name ="timezone" type ="System.Double" defaultValue ="0" />

< add name ="fontsize" type ="int" defaultValue ="0" />

</ properties >

</ profile >

< providers >
用Reflector打开MemberRole,可以看到相比Membership,profile在类结构方面复杂很多,其实往往数据库设计的越简单,处理数据的类就越复杂。与Membership一样,通过实现 IConfigurationSectionHandler接口来读取在Web.config中配置。先实例化一个ProfileConfig,用来存储Providers节点下的信息和Properties节点下信息。
Provider与Properties属性下,看看UML图:
public class ProfileConfig
{
//Methods
publicProfileConfig(ProfileConfigparent);

//Fields
publicboolAutomaticSaveEnabled;
publicboolEnabled;
publicstringInherits;
publicProfilePropertySettingsCollectionProperties;
publicProfileProviderProvider;
}

分别存储在
在图中,可以看到Properties是一个ProfilePropertySettingsCollection类的实例,该实例通过一个索引器来保存或者读取多个ProfilePropertySettings类实例,而ProfilePropertySettings实例中保存的就是<properties>节点下的<add>节点信息,如:<add name = "msnIM" type = "string" />。
由于Profile采用的也是Provider数据访问模型,所以可以看到上图中左边的三个类一路继承下来,但都是抽象类,没有具体实现。由于MemberRole中只实现了SQL Server的数据库存储实现,该实现在SqlProfileProvider类中可以看到:
(UML 中用斜体来表示抽象方法或者抽象类)
在Profile中还运用了httpModule,在web.config文件文件中,我们还看到这样一个配置文件:

httpModule模块的配置,该模块有何用处我们具体看看。

< httpModules >

……

< add name ="Profile" type ="Microsoft.ScalableHosting.Profile.ProfileModule,MemberRole,Version=1.0.0.0,Culture=neutral,PublicKeyToken=b7c773fb104e7562" />

……

</ httpModules >
这是一个
以下类省略方法与属性具体内容:
httpModule不了解,请看前面的专题。在Init方法中可以看到
public sealed class ProfileModule:IHttpModule
{
//Events
publiceventProfileMigrateEventHandlerMigrateAnonymous;
publiceventProfileEventHandlerPersonalize;
publiceventProfileAutoSaveEventHandlerProfileAutoSaving;

//Methods
staticProfileModule();
[SecurityPermission(SecurityAction.Demand,UnmanagedCode
=true)]
publicProfileModule();
publicvoidDispose();
publicvoidInit(HttpApplicationapp);
privatevoidOnEnter(objectsource,EventArgseventArgs);
privatevoidOnLeave(objectsource,EventArgseventArgs);
privatevoidOnPersonalize(ProfileEventArgse);
internalstaticvoidParseDataFromDB(string[]names,stringvalues,byte[]buf,SettingsPropertyValueCollectionproperties);
internalstaticvoidPrepareDataForSaving(refstringallNames,refstringallValues,refbyte[]buf,boolbinarySupported,SettingsPropertyValueCollectionproperties,booluserIsAuthenticated);

//Fields
privateProfileAutoSaveEventHandler_AutoSaveEventHandler;
privateProfileEventHandler_eventHandler;
privateProfileMigrateEventHandler_MigrateEventHandler;
privatestaticobjects_Lock;
}



如果你对
Http请求状态开始和结束Http请求时自动被激发。很有必要具体看看事件的处理内容:
public void Init(HttpApplicationapp)
{
app.AcquireRequestState
+=newEventHandler(this.OnEnter);
app.EndRequest
+=newEventHandler(this.OnLeave);
}


这里声明了两个事件,这两个事件分别在
Http请求状态开始时激发:
Context,这很重要,因为web请求是无状态的,要保存上下文信息来完成这次请求。接着调用OnPersonalize方法:
private void OnEnter( object source,EventArgseventArgs)
{
if(ProfileManager.Enabled)
{
HttpContextcontext1
=((HttpApplication)source).Context;
this.OnPersonalize(newProfileEventArgs(context1));
stringtext1=AnonymousIdUtil.GetAnonymousIdInternal(context1);
if((context1.Request.IsAuthenticated&&(text1!=null))&&((text1.Length>0)&&(this._MigrateEventHandler!=null)))
{
AnonymousIdUtil.SetShowAnonymousId(context1,
true);
ProfileMigrateEventArgsargs1
=newProfileMigrateEventArgs(context1,text1);
this._MigrateEventHandler(this,args1);
AnonymousIdUtil.SetShowAnonymousId(context1,
false);
}

}

}


首先得到该次请求的上下文
—Personalize(用处是在你的运用代码中如果还需要对这个请求开始的时候做一些个性化处理添加自己的方法,那么就可以通过向这个事件指定委托,委托指向的方法就会在这个时候被事件调用。)。之后判断e.Profile是否已经有内容,如果有就保存在context. Items[ "PRF"]中,如果没有就保存一个context. Items[ "PRFDL"] = true,这样后面就可以判断Profile的内容是否已经存储在请求的上下文中了。
private void OnPersonalize(ProfileEventArgse)
{
if(this._eventHandler!=null)
{
this._eventHandler(this,e);
}

if(e.Profile!=null)
{
ProfileUtil.SetProfile(e.Context,e.Profile);
}

else
{
ProfileUtil.SetProfileDelayLoad(e.Context,
true);
}

}


该方法主要是初始化一个事件
再回到OnEnter方法中来,处理完OnPersonalize方法的调用后,判断是否是是已经验证过的匿名用户,根据判断后在看是否进行MigrateAnonymous事件的初始化,关于匿名用户的处理,后面会专门的专题讲解,这里就不多说。
看看结束Http请求时激发的方法:
web.config中的设置判断是否初始化ProfileAutoSaving事件,然后ContinueWithProfileAutoSave的值来决定是否在请求结束的时候自动对Profile信息进行保存,其实这里的Profile信息就是一个ProfileBase实体,在OnPersonalize方法中被保存在context. Items[ "PRF"]中,当这次请求完成后,上下文context信息将自动被销毁。
private void OnLeave( object source,EventArgseventArgs)
{
if(ProfileManager.Enabled)
{
HttpApplicationapplication1
=(HttpApplication)source;
HttpContextcontext1
=application1.Context;
if(((ProfileUtil.GetProfileInternal(context1)!=null)&&(ProfileUtil.GetProfileInternal(context1)!=ProfileBase.SingletonInstance))&&ProfileManager.AutomaticSaveEnabled)
{
if(this._AutoSaveEventHandler!=null)
{
ProfileAutoSaveEventArgsargs1
=newProfileAutoSaveEventArgs(context1);
this._AutoSaveEventHandler(this,args1);
if(!args1.ContinueWithProfileAutoSave)
{
return;
}

}

ProfileUtil.GetProfileInternal(context1).Save();
}

}

}


其实只要对事件与委托了解,看懂就不成问题,主要就是根据在
在Profile中还有很重要的一部分,那就是数据的序化(串行化),通过一些方法把要存储的信息和信息的名称分别存放在数据库的两个字段中,这些信息是叠加的字符或者二进制串:
PropertyNames保存Profile的名称,PropertyValuesString保存以文本方式Profile的值,PropertyValuesBinary是二进制方式保存的Profile的值。具体分析一下:
PropertyNames内容:
“publicEmail:S:0:13:yahooIM:S:13:0:timezone:S:13:1:msnIM:S:14:13:commonName:B:0:-1:birthdate:S:27:9:gender:S:36:1:fontsize:B:0:-”
PropertyValuesString内容:
“ugoer@msn.com0ugoer@msn.com1982-8-141yyyy年M月d日, dddFalsezh-CN25694432www.ugoer.com无ugoer.cnblogs.comTrue”
“S”表示是用文本方式存储PropertyNames值,如果是“B”就表示用二进制存储。心细一点就会发现PropertyNames的值中包括所有的web.config中配置 < properties >节点下的name,只是在这些name后面多了如“:S:13:1:”这样的信息,刚才说过S表示用文本方式存储,其实“13”指的是从第PropertyValuesString字段中存储信息的第13个字符开始后的1个字符长度的内容为timezone的值。这就是奥妙所在,那么PropertyValuesBinary是不是多余呢?有时你可能考虑需要用二进制的方式来保存这些信息,这样在数据库中这些信息就不容易直接看到,因此MemberRole中的Profile提供这样的选择,在asp.net 2.0 beta2中也同样提供这样的选择,毕竟众口难调嘛。
最后看看是如何对properties信息进行序化和反序化的:
数据操作的具体实现是SqlProfileProvider类中,SetPropertyValues方法实现Profile信息的保存,该方法中调用ProfileModule类的PrepareDataForSaving方法来实现数据的序化(其实我搞不明白为什么PrepareDataForSaving这个方法放在ProfileModule类下,这有点不合逻辑,不过方法是静态的,放在哪里都不影响):
ParseDataFromDB方法中进行,该方法也在ProfileModule类下:
internal static void PrepareDataForSaving( ref string allNames, ref string allValues, ref byte []buf, bool binarySupported,SettingsPropertyValueCollectionproperties, bool userIsAuthenticated)
{
StringBuilderbuilder1
=newStringBuilder();
StringBuilderbuilder2
=newStringBuilder();
MemorySt
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值