CommunityServer中的Provider模式的应用
Provider的优势:
不关心具体实现
支持多数据库
方便文档描述交流
有利于团队协作分工
下图是程序的结构。

图1
下面给出具体的程序示例:
Blogs\Controls\Admin\BlogFeedbackAdmin.cs文件中的一个方法:
/// <exclude />
public override void DataBind()
{
base.DataBind ();
this.FeedbackList.DataKeyField = "PostID";
Int32 totalRecords;
this.FeedbackList.DataSource = BlogFeedback.GetFeedback( this.CurrentWeblog.SectionID, GridPager.PageIndex, this.GridPager.PageSize, out totalRecords );
//this.FeedbackList.VirtualItemCount = totalRecords;
this.GridPager.TotalRecords = totalRecords;
this.FeedbackList.DataBind();
}
这里用到了一个 BlogFeedback类,这个类的代码在CommunityServer.Blogs.Components的BlogFeedback.cs文件中:

BlogFeedback.cs的源代码
//------------------------------------------------------------------------------
// <copyright company="Telligent Systems">
// Copyright (c) Telligent Systems Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
using System;
using System.Collections;
using CommunityServer.Components;
namespace CommunityServer.Blogs.Components
{
/// <summary>
/// Manages the feedback that a blog post recieves.
/// </summary>
public sealed class BlogFeedback
{
private BlogFeedback()
{
// sealed static class
}
/// <summary>
/// Deletes blog feedback by postIDs
/// </summary>
public static void DeleteFeedback( Int32 userID, Int32 sectionID, Int32[] postIDs )
{
foreach( Int32 postID in postIDs )
{
WeblogPosts.Delete(sectionID, postID, BlogPostType.Comment, userID);
}
}
/// <summary>
/// Sets the given feedback items to moderated
/// </summary>
public static void ModerateFeedback( Int32 userID, Int32 weblogID, Int32[] postIDs )
{
foreach( Int32 postID in postIDs )
{
WeblogPost p = WeblogPosts.GetPost(postID, false, false, true );
if(weblogID == p.SectionID && p.IsApproved )
{
p.IsApproved = false;
WeblogPosts.Update(p, userID );
}
}
}
/// <summary>
/// Sets the given feedback items to approved
/// </summary>
public static void ApproveFeedback( Int32 userID, Int32 weblogID, Int32[] postIDs )
{
foreach( Int32 postID in postIDs )
{
WeblogPost p = WeblogPosts.GetPost( postID, false, false, true );
if(weblogID == p.SectionID && !p.IsApproved )
{
p.IsApproved = true;
WeblogPosts.Update( p, userID );
}
}
}
/// <summary>
/// Gets the feedback items for the given Weblog
/// </summary>
/// <returns>Returns an ArrayList of FeedBackItem objects.</returns>
public static ArrayList GetFeedback( Int32 weblogID )
{
int outRecords;
return GetFeedback( weblogID,0,0, out outRecords);
}
/// <summary>
/// Gets a paged set of feedback items for the given weblogs
/// </summary>
/// <returns>Returns an ArrayList of FeedBackItem objects.</returns>
public static ArrayList GetFeedback( Int32 blogId, Int32 pageIndex, Int32 pageSize, out Int32 totalRecords )
{
WeblogDataProvider wdp = WeblogDataProvider.Instance();
return wdp.GetFeedback( blogId,pageIndex, pageSize, out totalRecords);
}
}
}
注意此文件的
/// <summary>
/// Gets a paged set of feedback items for the given weblogs
/// </summary>
/// <returns>Returns an ArrayList of FeedBackItem objects.</returns>
public static ArrayList GetFeedback( Int32 blogId, Int32 pageIndex, Int32 pageSize, out Int32 totalRecords )
{
WeblogDataProvider wdp = WeblogDataProvider.Instance();
return wdp.GetFeedback( blogId,pageIndex, pageSize, out totalRecords);
}
以上部分对应图1的Components
在这个文件中的WeblogDataProvider类就是DataProvider抽象类,下面看看这个类的类图:

下面是这个类的源代码:


//------------------------------------------------------------------------------
// <copyright company="Telligent Systems">
// Copyright (c) Telligent Systems Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------

using System;
using System.Collections;
using System.Data;
using System.IO;
using System.Xml;
using CommunityServer.Components;
using CommunityServer.Configuration;

namespace CommunityServer.Blogs.Components


{

/**//// <summary>
/// Summary description for WeblogDataProvider.
/// </summary>
public abstract class WeblogDataProvider

{
public static readonly string WeblogDataProviderName = "WeblogDataProvider";


Search#region Search
public abstract SearchResultSet GetSearchResults(SearchQuery query, SearchTerms terms);
public abstract PostSet SearchReindexPosts (int setsize, int settingsID);
#endregion


Instance#region Instance

private static WeblogDataProvider _defaultInstance = null;

static WeblogDataProvider()

{
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 WeblogDataProvider Instance()

{
return _defaultInstance;
}

public static WeblogDataProvider Instance (Provider dataProvider)

{
WeblogDataProvider fdp = CSCache.Get(dataProvider.Name) as WeblogDataProvider;
if(fdp == null)

{
fdp = DataProviders.Invoke(dataProvider) as WeblogDataProvider;
CSCache.Max(dataProvider.Name,fdp);
}
return fdp;
}


/**//// <summary>
/// Creates the Default CommonDataProvider
/// </summary>
private static void CreateDefaultCommonProvider()

{
// Get the names of the providers
//
CSConfiguration config = CSConfiguration.GetConfig();

// Read the configuration specific information
// for this provider
//
Provider sqlForumsProvider = (Provider) config.Providers[WeblogDataProviderName];

// Read the connection string for this provider
//
_defaultInstance = DataProviders.CreateInstance(sqlForumsProvider) as WeblogDataProvider;
}
#endregion


Base Methods#region Base Methods


Get Posts#region Get Posts
public abstract WeblogPost GetWeblogPost(int postID, bool includeCategories, bool isApproved);
public abstract ThreadSet GetBlogThreads(BlogThreadQuery query);
public abstract PostSet GetPosts(BlogPostQuery query);
public abstract PostSet GetThreadsTrackedByUser(int SectionID, int UserID);
#endregion


Posts#region Posts
public abstract BlogPostResults AddPost(WeblogPost post, User user, out int PostID);
public abstract BlogPostResults UpdatePost(WeblogPost post, int editedBy);
public abstract void DeletePost(int sectionID, int postID, int userID, BlogPostType blogPostType);
#endregion


GetBlogs#region GetBlogs
public abstract Hashtable GetWeblogs();
#endregion


Permissions#region Permissions
public abstract ArrayList GetBlogPermissions(int weblogID);
#endregion


Blog Feedback#region Blog Feedback

public abstract ArrayList GetFeedback( Int32 forumID, Int32 pageIndex, Int32 pageSize, out Int32 totalRecords );

#endregion

public abstract ArrayList GetPostsByMonths(int BlogID);
public abstract Hashtable GetPostsByMonth(int BlogID, DateTime Month);

public abstract void UpdateRecentContent(int SettingsID);
public abstract void WeblogPostToForumPost(int postID, int forumID, int SettingsID);

#endregion


Helpers#region Helpers

public static object ConvertCategoriesToXML(string[] Categories)

{
if(Categories == null || Categories.Length == 0)
return DBNull.Value;

StringWriter sw = new StringWriter();
XmlTextWriter writer = new XmlTextWriter(sw);
writer.WriteStartElement("Categories");
foreach (string category in Categories)

{
writer.WriteElementString("Category", category);
}
writer.Close();
return sw.ToString();
}

#endregion


Populate#region Populate


Weblogs#region Weblogs


/**//// <summary>
/// Populates an existing Weblog object
/// </summary>
public static void PopulateWeblogData(IDataReader dr, Weblog w)

{
CommonDataProvider.PopulateSectionFromIDataReader(dr,w);

w.MostRecentPostName = dr["MostRecentPostName"] as string;

w.MostRecentArticleID = (int)dr["MostRecentArticleID"];
w.MostRecentArticleDate = (DateTime)dr["MostRecentArticleDate"];
w.MostRecentArticleAuthor = dr["MostRecentArticleAuthor"] as string;
w.MostRecentArticleAuthorID = (int)dr["MostRecentArticleAuthorID"];
w.MostRecentArticleName = dr["MostRecentArticleName"] as string;
w.PostCount = (int)dr["PostCount"];
w.ArticleCount = (int)dr["ArticleCount"];
w.TrackbackCount = (int)dr["TrackbackCount"];
w.CommentCount = (int)dr["CommentCount"];
}

#endregion


public static WeblogPost PopulateWeblogPostContentFromIDataReader(IDataReader reader)

{
//Is this all of the data we need?
WeblogPost post = new WeblogPost();


post.SectionID = (int) reader["SectionID"];
post.PostID = (int) reader["PostID"];
post.Replies = (int) reader["Replies"];
post.Views = (int) reader["TotalViews"];
post.AggViews = (int) reader["AggViews"];
post.ThreadID = (int) reader["ThreadID"];
post.Subject = (string) reader["Subject"];
post.Body = (string) reader["Body"];
post.Username = (string) reader["Username"];
post.AuthorID = (int) reader["UserID"];
post.PostDate = (DateTime) reader["PostDate"];
post.BloggerTime = (DateTime) reader["BloggerTime"];
post.ThreadDate = (DateTime) reader["ThreadDate"];
post.IsLocked = (bool) reader["IsLocked"];
post.IsApproved = (bool) reader["IsApproved"];
post.FormattedBody = reader["FormattedBody"] as string;
post.ThreadID = (int)reader["ThreadID"];

post.PostLevel = (int) reader["PostLevel"];
post.ParentID = (int) reader["ParentID"];

post.PostConfig = (BlogPostConfig) (int)reader["PostConfig"];
post.BlogPostType = (BlogPostType) (byte)reader["BlogPostType"];

post.TitleUrl = reader["TitleUrl"] as string;
post.Excerpt = reader["Excerpt"] as string;
post.Name = reader["PostName"] as string;

post.RatingSum = (int)reader["RatingSum"];
post.TotalRatings = (int)reader["TotalRatings"];

return post;
}

public static WeblogPost PopulateWeblogPostContentFromDataRow(DataRow dr, DataRelation relationShip)

{
//Is this all of the data we need?
WeblogPost post = new WeblogPost();


post.SectionID = (int) dr["SectionID"];
post.PostID = (int) dr["PostID"];
post.Replies = (int) dr["Replies"];
post.Views = (int) dr["TotalViews"];
post.AggViews = (int) dr["AggViews"];
post.ThreadID = (int) dr["ThreadID"];
post.Subject = (string) dr["Subject"];
post.Body = (string) dr["Body"];
post.Username = (string) dr["Username"];
post.AuthorID = (int) dr["UserID"];
post.PostDate = (DateTime) dr["PostDate"];
post.BloggerTime = (DateTime) dr["BloggerTime"];
post.ThreadDate = (DateTime) dr["ThreadDate"];
post.IsLocked = (bool) dr["IsLocked"];
post.EmoticonID = (int) dr["ThreadEmoticonID"];
post.FormattedBody = dr["FormattedBody"] as string;

post.IsApproved = (bool) dr["IsApproved"];


post.PostLevel = (int) dr["PostLevel"];
post.ParentID = (int) dr["ParentID"];

post.PostConfig = (BlogPostConfig) (int)dr["PostConfig"];
post.BlogPostType = (BlogPostType) (byte)dr["BlogPostType"];

post.TitleUrl = dr["TitleUrl"] as string;
post.Excerpt = dr["Excerpt"] as string;
post.Name = dr["PostName"] as string;

post.RatingSum = (int)dr["RatingSum"];
post.TotalRatings = (int)dr["TotalRatings"];

DataRow[] categories = dr.GetChildRows(relationShip);
if(categories != null && categories.Length > 0)

{
post.Categories = new string[categories.Length];
for(int i = 0; i<categories.Length; i++)

{
post.Categories[i] = categories[i]["Name"] as string;
}
}

return post;
}


public static WeblogPost PopulateWeblogEntryFromIDataReader(IDataReader reader)

{
WeblogPost entry = PopulateWeblogPostContentFromIDataReader(reader);
SerializerData data = CommonDataProvider.PopulateSerializerDataIDataReader(reader, SerializationType.Post);
entry.SetSerializerData(data);

return entry;


// CommonDataProvider.PopulatePostFromIDataReader(reader,entry);
//
// entry.PostConfig = (BlogPostConfig) (int)reader["PostConfig"];
// entry.BlogPostType = (BlogPostType) (byte)reader["BlogPostType"];
//
// entry.BloggerTime = (DateTime) reader["BloggerTime"];
//
// entry.TitleUrl = reader["TitleUrl"] as string;
// entry.Excerpt = reader["Excerpt"] as string;
// entry.Name = reader["PostName"] as string;
//
// entry.RatingSum = (int)reader["RatingSum"];
// entry.TotalRatings = (int)reader["TotalRatings"];
}



#endregion

}
}

可以看到,这个类是抽象类,类中除了构造函数和Instance方法以及画实现的方法以外,其余的都是抽象方法。构造函数和
Instance方法的作用已经在CommunityServerBlogs/BlogFeedback.cs文件分析中论述过。
以上部分对应图1中的Data Provider APIs部分
DataProvider API的实现类:CommunityServer.Data中的WeblogSqlDataProvider.cs文件:

下面看看其中的一个具体的方法:
/// <summary>
/// Returns blog comments/trackbacks
/// </summary>
public override ArrayList GetFeedback(int forumID, int pageIndex, int pageSize, out int totalRecords)
{
ArrayList feedback = new ArrayList();
using ( SqlConnection cn = GetSqlConnection() )
using ( SqlCommand cmd = new SqlCommand( databaseOwner + ".cs_weblog_Feedback_Get", cn ) )
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@SectionID", SqlDbType.Int).Value = forumID;
cmd.Parameters.Add(this.SettingsIDParameter());
cmd.Parameters.Add("@TotalRecords", SqlDbType.Int ).Direction = ParameterDirection.Output;
if ( pageSize > 0 )
{
cmd.Parameters.Add( "@UsePaging", SqlDbType.Bit ).Value = true;
cmd.Parameters.Add( "@PageSize", SqlDbType.Int ).Value = pageSize;
cmd.Parameters.Add( "@PageIndex", SqlDbType.Int ).Value = pageIndex;
}
cn.Open();
using(SqlDataReader dr = cmd.ExecuteReader(CommandBehavior.CloseConnection))
{
// PostID, PostAuthor, Subject, PostDate, IsApproved, PostName, TitleUrl
while ( dr.Read() )
{
BlogFeedbackItem item = new BlogFeedbackItem();
item.PostID = (Int32)dr[ "PostID" ];
item.PostAuthor = (String)dr[ "PostAuthor" ];
item.Subject = (String)dr[ "Subject" ];
item.IsApproved = (Boolean)dr[ "IsApproved" ];
item.TitleUrl = dr[ "TitleUrl" ] as String;
item.PostDate = (DateTime)dr[ "PostDate" ];
item.Body = dr[ "Body" ] as String;
feedback.Add( item );
}
dr.Close();
}
cn.Close();
totalRecords = (Int32)cmd.Parameters[ "@TotalRecords" ].Value;
}
return feedback;
}
相关方法:
protected SqlConnection GetSqlConnection ()
{
try
{
return new SqlConnection(ConnectionString);
}
catch
{
throw new CSException(CSExceptionType.DataProvider, "SQL Connection String is invalid.");
}
}
以上部分对应图1中的Data Provider APIs
可以看到,每次执行GetFeedback方法的时候,都打开一个新的数据库连接,然后操作数据库。操作完成后关闭数据库连接。(我想这样可能产生一个问题,就是如果在线人数很多的时候,就会有很多的数据库连接,这样可能会导致程序运行缓慢,是否可以采用Singleton模式的变种来控制数据库连接的数量?)
参考资料:CommunityServer架构分析,作者:宝玉(
http://blog.joycode.com/dotey;http://www.communityserver.cn )
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架