下载CnForumsBeta1(今天发现CnForum1.2测试版本已经发布,可以至开发实验室下载,或者访问宝玉的blog)已经有段时间,一直想看看其中的代码,但是它的项目结构实在是复杂(也是本人能力有限),实在是觉得无从下手。
最近由于项目的原因对如何统计在线用户信息很感兴趣,正好利用这个机会学习一下CnForum的实现方式。
BTW:用Google搜了一下“asp.net 用户统计”(如google搜索结果),结果发现前几篇文章的内容几乎相同,只是稍微修改了某些字和出处而已,只是有意思。看样子真是天下文章一大“抄”啊!当然这不是今天讨论的重点,下面切入主题。
首先就是选取切入点。在安装CnForum后的论坛首页左下方有用户在线信息的统计,显示效果如图:
当用户点击“用户在线信息”时,将弹出显示详细的用户在线信息。查看“用户在线信息”链接发现是指向ViewOnline.aspx的。那么就从ViewOnline.aspx页面开始着手吧。
查看ViewOnline.aspx页面的HTML代码,发现主要功能实现是AspNetForums.Controls下的WhoIsOnlineView控件展现的。
进一步查看WhoIsOnlineView控件代码(在子项目Controls下)。
WhoIsOnlineView类是继承SkinnedForumWebControl类的,从名称来看SkinnedForumWebControl类应该不是重点。重点是WhoIsOnlineView类中重写的InitializeSkin(Control skin)方法。从方法中的代码知道是使用repeat控件来显示注册会员和游客信息。
由于获取注册会员和游客信息的方法应该是雷同的,所以这里只需要分析在线注册会员的获得即可。
从代码 repeater.DataSource = Users.GetUsersOnline(15);知道在线注册会员数据的获得是通过Users类的GetUsersOnline(int pastMinutes)方法得到。
定位到Users类(子项目Components下)的GetUsersOnline方法,进而发现实际是调用Users类的GetMembersGuestsOnline(int pastMinutes)方法获得数据。
检查GetMembersGuestsOnline方法的函数体,其中对获取在线注册会员数据的关键代码应该是:
ForumsDataProvider dp = ForumsDataProvider.Instance();
users = dp.WhoIsOnline(pastMinutes);
分析ForumsDataProvider dp = ForumsDataProvider.Instance()
定位至ForumsDataProvider类(子项目Components中Provider下)的静态方法Instance()->静态方法Instance (HttpContext context, string providerTypeName, string databaseOwner, string connectionString),目的是返回一个ForumsDataProvider类的实例。
那么ForumsDataProvider的Instance方法到底完成了什么呢?
首先通过ForumConfiguration config = ForumConfiguration.GetConfig();获得ForumConfirguration类的对象。定位至ForumConfirguration的GetConfig方法。
奇怪的事情出现了:GetConfig方法中只有简单的一句(ForumConfiguration) ConfigurationSettings.GetConfig("forums/forums"),但是从ForumConfirguration类定义来看私有成员有很多(如providers、defaultProvider等),而且回头看看
ForumsDataProvider的Instance方法中在调用GetConfig方法后就使用了config的providers。那这些私有成员到底是怎样初始化的呢?看样子玄机就在ConfigurationSettings.GetConfig方法上。查看MSDN知道这个方法是用来“返回用户定义的配置节的配置设置”,查看Web.config的配置,发现forums/forums节是<section name="forums" type="AspNetForums.Configuration.ForumsConfigurationHandler, AspNetForums.Components" />。
在MSDN中搜索section,有这样一段话“声明配置节实质上为配置文件定义了新元素。新元素包含配置节处理程序(即实现
搜索项目,果然在ForumConfiguration.cs文件(子项目Components/Confirguration下)找到了类ForumsConfigurationHandler,它实现了接口IConfigurationSectionHandler。搜索MSDN知道IConfigurationSectionHandler接口有方法Create,可以返回用户自己的配置对象。
回到代码中,在ForumsConfigurationHandler中用户获得了ForumConfiguration对象,并且通过方法LoadValuesFromConfigurationXml初始化了ForumConfiguration对象的私有成员。
哦,原来如此!看似简单的一句return (ForumConfiguration) ConfigurationSettings.GetConfig("forums/forums")其实已经填充了所需的配置内容。
[是不是很罗嗦啊,不要拿砖头砸我哦!喝口水先~~~]
回到ForumsDataProvider类的Instance方法中,随后的代码就是获得缺省配置内容(详细情况可以查看Web.config),包括数据库连接字符串、数据库Owner等配置信息,以及关键的缺省Provider类型名称providerTypeName,在我的项目配置中是“AspNetForums.Data.SqlDataProvider”,记住这个名字,它随后将起到关键作用!
继续>>根据代码,当Cache["DataProvider"]为null时,获得与前面得到的providerTypeName相应的Type,下面有这样一句代码:cache.Insert( "DataProvider", type.GetConstructor(paramTypes) );。
问题来了,这段代码是做什么的呢?
搜索MSDN知道Type.GetConstructor 方法是“获取当前Type的特定构造函数”(这里说的特定就是指paramTypes,即有两个string参数的构造函数)。根据前面知道type的类型是AspNetForums.Data.SqlDataProvider,那就意味着应该有个类,名字是SqlDataProvider,并且有个样子是SqlDataProvider(string param1,string param2)的构造函数。
搜索项目...果然在子项目SqlDataProvider下的有个名为SqlDataProvider的类,并且构造函数为SqlDataProvider(string databaseOwner, string connectionString)。原来cache.Insert( "DataProvider", type.GetConstructor(paramTypes) )是把与WebConfig中配置的defaultProvider(这里是SqlDataProiver,继承ForumsDataProvider)相应的ConstructorInfo,通过这个ConstructInfo对象我们可以调用SqlDataProvider的构造函数来获得SqlDataProvider对象。
这样做的好处是显而易见的,我们只需要修改Web.config就可以灵活的修改具体访问数据所使用的类。
然后通过ConstructorInfo的Invoke方法获得了SqlDataProvider对象。
object[] paramArray = new object[2];
paramArray[0] = databaseOwner;
paramArray[1] = connectionString;
return (ForumsDataProvider)( ((ConstructorInfo)cache["DataProvider"]).Invoke(paramArray) );
至此,通过ForumsDataProvider dp = ForumsDataProvider.Instance()实际获得了SqlDataProvider对象,而在SqlDataProvider类中通过重写实现了ForumsDataProvider中定义的所有abstract方法,即实现了所有需要的数据访问方法。
分析users = dp.WhoIsOnline(pastMinutes)
既然访问数据的对象已经准备完毕了,users = dp.WhoIsOnline(pastMinutes)也就变成了仅仅是调用SqlDataProvider对象的方法而已了。
找到SqlDataProvider类的WhoIsOnline方法,即public override Hashtable WhoIsOnline(int pastMinutes)
这个方法其实就是调用存储过程forums_Users_Online,查看这个存储过程(这个存储过程有输入参数@PastMinutes,此处值为15)。
发现它首先会删除最近一个小时都没有Activity(活动)的在线用户信息,即:
DELETE
forums_UsersOnline
WHERE
LastActivity < DateAdd(hour, -1, GetDate())
然后根据表forums_UsersOnline中的数据找出最近15分钟有Activity(活动)注册会员信息,以及从表forums_AnonymousUsers中找出所有在线游客信息。
OK,对CnForum中在线用户信息统计的实现方式就分析到这里。
感言:阅读高手的代码的确是收益匪浅,写这个Post也希望能够起到抛砖引玉的效果,共同进步吧~
在分析的过程中难免有错误的地方,请见谅。 当然也欢迎大家扔砖..