community server 学习与分析 之二

来源:互联网 发布:北京时间自动校准软件 编辑:程序博客网 时间:2024/06/06 17:33

 

现在以帖子的数据是如何加载的线索来读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 );
大致就是如此了。
原创粉丝点击