现在以帖子的数据是如何加载的线索来读CommunityServer的代码。
根据上一篇BLOG的分析,知道加载数据是PostFlatView类负责的,在这个类重载的OnInit方法里:
protected override void OnInit(EventArgs e)
{
if (SkinName == null)
ExternalSkinFileName = "View-PostFlatView.ascx";
else
ExternalSkinFileName = SkinName;
this.csContext = CSContext.Current;
this.currentUser = csContext.User;
this.post = Posts.GetPost( csContext.PostID, currentUser.UserID, true, true );
..................
在这里我只关心如何加在数据,后面的东西也很重要(论坛的界面???),以后再继续分析。
..................
}
this.post = Posts.GetPost( csContext.PostID, currentUser.UserID, true, true ); 这短短的一行就把数据加载了,但是,为了弄清楚运行的脉络,继续前往Posts.GetPost方法。在调用此方法的地方直接点击右键,转到定义,就打开了Post.cs 。 这个类提供了几个不同参数的方法来取得数据,但最终都是通过public static ForumPost GetPost(int postID, int userID, bool trackViews, bool markRead, bool includeCategories){}方法取得的。具体分析这个方法。
public static ForumPost GetPost(int postID, int userID, bool trackViews, bool markRead, bool includeCategories)
{
CSContext csContext = CSContext.Current;
//这是CS设计的当前请求的上下文类,包括了一个当前请求的httpContext及CS系统里的一些信息. CSContext类是一个sealed类,它提供了一个静态的Create 方法来返回它的实例,在这个方法中,会对它自己的一些成员进行初始化.
string key = "Post" + postID + ":" + includeCategories;
if (csContext.Items[key] != null && !trackViews)
{ //如果context里有这个帖子,且没有使用trackviews,则直接返回当前Request的cscontext中缓存的帖子
return (ForumPost) csContext.Items[key];
}
else
{//否则,就要从数据库中读数据了.这个可是个绕来绕去的过程.
ForumPost post;
ForumDataProvider dp = ForumDataProvider.Instance();
post = dp.GetPost( postID, userID, trackViews, markRead, includeCategories );
// Store in context of current request
csContext.Items[key] = post;
return post;
}
}
首先,声明了一个post对象,最终返回的就是它了.接着, ForumDataProvider dp = ForumDataProvider.Instance(); 这一句很重要,我们打开ForumDataProvider类:
namespace CommunityServer.Discussions.Components
{
public abstract class ForumDataProvider
{
public ForumDataProvider()
{
}
public static readonly string ForumDataProviderName = "ForumDataProvider";
public abstract PostSet SearchReindexPosts (int setsize, int settingsID);
public abstract ThreadSet SearchIndexPosts (int setsize, ForumThreadQuery query);
#region Instance
private static ForumDataProvider _defaultInstance = null;
static ForumDataProvider()
{
CreateDefaultCommonProvider();
}
///<summary>
/// Returns an instance of the user-specified data provider class.
///</summary>
///<returns>An instance of the user-specified data provider class. This class must inherit the
/// CommonDataProvider interface.</returns>
public static ForumDataProvider Instance()
{
return _defaultInstance;
}
public static ForumDataProvider Instance (Provider dataProvider)
{
ForumDataProvider fdp = CSCache.Get(dataProvider.Name) as ForumDataProvider;
if(fdp == null)
{
fdp = DataProviders.Invoke(dataProvider) as ForumDataProvider;
CSCache.Max(dataProvider.Name,fdp);
}
return fdp;
}
///<summary>
/// Creates the Default CommonDataProvider
///</summary>
private static void CreateDefaultCommonProvider()
{
// Get the names of the providers
//
1.CSConfiguration config = CSConfiguration.GetConfig();
// Read the configuration specific information
// for this provider
2.Provider sqlForumsProvider = (Provider) config.Providers[ForumDataProviderName];
// Read the connection string for this provider
3. _defaultInstance = DataProviders.CreateInstance(sqlForumsProvider) as ForumDataProvider;
}
………………………………………………
}
}
在Instance方法里,直接return _defaultInstance,
而这个_defaultInstance是ForumDataProvider类型的,并且是ForumDataProvider类的一个成员变量: private static ForumDataProvider _defaultInstance = null;
在生成这个对象的时候,会生成它的实例,并且在它的静态的构造方法里调用了CreateDefaultCommonProvider()方法.仔细看这个方法,其中有很大的玄虚.
1. CSConfiguration config = CSConfiguration.GetConfig();
创建了一个CSConfiguration类的实例,这个类是整个CS系统的配置类,它读取并存储communityServer.config里的配置信息, CSConfiguration.GetConfig()方法的代码如下:
public static CSConfiguration GetConfig()
{
CSConfiguration config = CSCache.Get(CacheKey) as CSConfiguration;
if(config == null)
{
string path = null;
HttpContext context = HttpContext.Current;
if(context != null)
path = context.Server.MapPath("~/communityserver.config");
else
path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "communityserver.config");
XmlDocument doc = new XmlDocument();
doc.Load(path);
config = new CSConfiguration(doc);
CSCache.Max(CacheKey,config,new CacheDependency(path));
CSCache.ReSetFactor(config.CacheFactor);
}
return config;
}
上述代码的逻辑很清晰,首先在当前的缓存里查找有没有CSconfiguration类的config实例,如果没有,就使用当前httpContext.MapPath方法找到程序的路径,构造CommunityServer.config文件的物理路径,然后把communityserver.config配置文件的信息赋给config,然后返回它.在这里,要特别的提下,CSConfiguration的结构,它的成员变量基本上和CommunityServer.config的节点是一样的,在CommunityServer.config文件里,如果它的子节点还有子节点,那么,在CSconfiguration类里使用一个Hashtable来代表这个子节点,然后在这个Hashtable里存储子节点的子节点。特别地,比如是Provider子节点,它是根据子节点里的配置信息,创建了Provider类的对象,放入了Hashtable。所以在2里可以强制转换成Provider类型。
2. Provider sqlForumsProvider = (Provider) config.Providers[ForumDataProviderName];
经过上面的一句得到config实例后,再由ForumDataProviderName成员变量(FormDataProvider类的成员变量)的值获得真正的Provider,它存储在communityserver.config文件中.具体就是这个节点.
<add name = "ForumDataProvider" type = "CommunityServer.Data.ForumsSqlDataProvider, CommunityServer.SqlDataProvider" connectionStringName = "SiteSqlServer" databaseOwnerStringName = "SiteSqlServerOwner"/>
从这里可以看到Forum的数据提供者的Type是"ForumSqlDataProvider"
3_defaultInstance = DataProviders.CreateInstance(sqlForumsProvider) as ForumDataProvider;
_defaultInstance的类型是ForumDataProvider,好,先放到这里。看后面一部分,首先看DataProviders.CreateInstance()方法。DataProviders是一个sealed修饰的类,即不能被继承。这个类也很重要,它的作用是装载和管理整个CS系统的DataProvider。还是只关注感兴趣的DataProviders.CreateInstance()方法。此方法的代码如下:
public static object CreateInstance(Provider dataProvider)
{
string connectionString = null;
string databaseOwner = null;
GetDataStoreParameters(dataProvider, out connectionString, out databaseOwner);
Type type = Type.GetType(dataProvider.Type);
object newObject = null;
if(type != null)
{
newObject = Activator.CreateInstance(type,new object[]{databaseOwner,connectionString});
}
if(newObject == null)
ProviderException(dataProvider.Name);
return newObject;
}
GetDataStoreParameters(dataProvider, out connectionString, out databaseOwner);这行代码的作用是根据参数dataProvider获取connectionString和databaseOwner信息。
Type type = Type.GetType(dataProvider.Type);创建一个dataProvider指定的一个Type。
下面的几行就很关键了,
object newObject = null;
if(type != null)
{
newObject = Activator.CreateInstance(type,new object[]{databaseOwner,connectionString});
}
这里用到了反射的机制,通过Activator.CreateInstance方法,根据指定的参数,创建并初始化了一个类的实例,并且根据前面的分析,应该能知道,这个实例是ForumsSqlDataProvider类的实例。再返回去看看ForumsSqlDataProvider类的定义,它是继承自ForumsDataProvider类的。
public class ForumsSqlDataProvider : ForumDataProvider{}
这一部分写的很详细,实际上,我觉得这块很能说明问题,CS为什么要这么设计,绕来绕去的不是很麻烦吗,这样有什么好处?还是有的。CS把一些系统的配置都放到一个XML文件里,然后在系统运行时,通过读取配置文件,来获取各种信息,比如数据提供者的信息,如果以后想换成ACCESS的数据库,或者Mysql的数据库,只需写访问相应数据库的组件,然后修改下配置文件,就可以直接使用其他的数据库了。说到底,还是为了便于以后的维护和升级。
到此,数据已经可以从数据库中读取出了。
ForumDataProvider dp = ForumDataProvider.Instance();
post = dp.GetPost( postID, userID, trackViews, markRead, includeCategories );
大致就是如此了。