设为首页 收藏本站
查看: 919|回复: 0

[经验分享] Community Server专题九:MemberRole之Profile

[复制链接]

尚未签到

发表于 2016-11-10 05:35:49 | 显示全部楼层 |阅读模式
上个专题我们讲到了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中设置日期格式如下:
DSC0000.jpg
可以看到CS论坛中的日期格式如下:
DSC0001.jpg
接下来,设置过另外一个格式:
DSC0002.jpg
在论坛中可以看到格式也改变了:
DSC0003.jpg
如果你注销后,日期格式又会发生改变(改变为默认日期格式),也就是说这种设置完全是个人的。
当然,要实现这样的功能需要构建复杂的代码,Profile机制在这里仅仅提供数据的存储服务,这个专题我们只分析Profile机制是如何提供存储服务的。
先看看配置文件:
节点下的内容我就不多说了。<properties>节点是配置的关键,该节点下面的信息就是一个注册用户可以存储的个性化信息。name是存储的名称,type是该名称保存数据的类型(其实还有一些信息可有可无,比如defaultValue等,表示默认值)。
DSC0004.gif <profileenabled="true">

<providers>

<add

name="CommunityServerSqlProvider"

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

connectionStringName
="SiteSqlServer"

applicationName
="dev"

description
="StoresandretrievesprofiledatafromthelocalMicrosoftSQLServerdatabase"

/>

</providers>



<properties>

<addname="commonName"type="string"/>

<addname="birthdate"type="DateTime"/>

<addname="gender"type="int"defaultValue="0"/>

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

<addname="publicEmail"type="string"/>

<addname="language"type="string"/>

<addname="webAddress"type="string"/>

<addname="webLog"type="string"/>

<addname="signature"type="string"/>

<addname="signatureFormatted"type="string"/>

<addname="location"type="string"/>

<addname="occupation"type="string"/>

<addname="interests"type="string"/>

<addname="msnIM"type="string"/>

<addname="yahooIM"type="string"/>

<addname="aolIM"type="string"/>

<addname="icqIM"type="string"/>

<addname="qqIM"type="string"/>

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

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

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

<addname="fontsize"type="int"defaultValue="0"/>

</properties>

</profile>


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

//Fields
publicboolAutomaticSaveEnabled;
publicboolEnabled;
publicstringInherits;
publicProfilePropertySettingsCollectionProperties;
publicProfileProviderProvider;
DSC0009.gif }


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

……

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

……

</httpModules>

这是一个
以下类省略方法与属性具体内容:
httpModule不了解,请看前面的专题。在Init方法中可以看到
publicsealedclassProfileModule: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请求时自动被激发。很有必要具体看看事件的处理内容:
publicvoidInit(HttpApplicationapp)
{
app.AcquireRequestState
+=newEventHandler(this.OnEnter);
app.EndRequest
+=newEventHandler(this.OnLeave);
}



这里声明了两个事件,这两个事件分别在
Http请求状态开始时激发:
Context,这很重要,因为web请求是无状态的,要保存上下文信息来完成这次请求。接着调用OnPersonalize方法:
privatevoidOnEnter(objectsource,EventArgseventArgs)
{
if(ProfileManager.Enabled)
DSC00012.gif DSC00013.gif
{
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);
DSC00014.gif }

}

}



首先得到该次请求的上下文
—Personalize(用处是在你的运用代码中如果还需要对这个请求开始的时候做一些个性化处理添加自己的方法,那么就可以通过向这个事件指定委托,委托指向的方法就会在这个时候被事件调用。)。之后判断e.Profile是否已经有内容,如果有就保存在context.Items["PRF"]中,如果没有就保存一个context.Items["PRFDL"] = true,这样后面就可以判断Profile的内容是否已经存储在请求的上下文中了。
privatevoidOnPersonalize(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信息将自动被销毁。
privatevoidOnLeave(objectsource,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中还有很重要的一部分,那就是数据的序化(串行化),通过一些方法把要存储的信息和信息的名称分别存放在数据库的两个字段中,这些信息是叠加的字符或者二进制串:
DSC00015.jpg
DSC00016.jpg

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类下:
internalstaticvoidPrepareDataForSaving(refstringallNames,refstringallValues,refbyte[]buf,boolbinarySupported,SettingsPropertyValueCollectionproperties,booluserIsAuthenticated)
{
StringBuilderbuilder1
=newStringBuilder();
StringBuilderbuilder2
=newStringBuilder();
MemorySt

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-298053-1-1.html 上篇帖子: Transact-SQL 系统变量 @@ 下篇帖子: SQL 分页支持查询
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表