在上一篇文章中,我谈到了asp.net Forums 之控件研究,这次我们来谈谈asp.net Forums 的配置、缓存和多数据访问。
一、配置
ANF的配置是通过ForumConfiguration类,ForumConfiguration类位于AspNetForums.Configuration命名空间下。
#region VSS
/*
* $Header: /HiForums/Components/Configuration/ForumConfiguration.cs 1 05-10-26 15:04 Jacky $
*
* $History: ForumConfiguration.cs $
*
* ***************** Version 1 *****************
* User: Jacky Date: 05-10-26 Time: 15:04
* Created in $/HiForums/Components/Configuration
*
* ***************** Version 1 *****************
* User: Jacky Date: 05-10-21 Time: 14:20
* Created in $/HiForums/Components/Configuration
*
* ***************** Version 3 *****************
* User: Jacky Date: 05-09-20 Time: 19:23
* Updated in $/ASP.NET Forums/Components/Configuration
*
* ***************** Version 2 *****************
* User: Jacky Date: 05-09-20 Time: 1:36
* Updated in $/ASP.NET Forums/Components/Configuration
*/
#endregion
using System;
using System.Collections;
using System.Collections.Specialized;
using System.IO;
using System.Web;
using System.Web.Caching;
using System.Xml;
using System.Configuration;
using System.Xml.Serialization;
using AspNetForums.Components;
using AspNetForums.Enumerations;
namespace AspNetForums.Configuration
{
// *********************************************************************
// ForumConfiguration
//
/// <summary>
/// 读取论坛配置信息,根据CS结构重构.
/// by venjiang 2005/10/25
/// </summary>
///
// ***********************************************************************/
public class ForumConfiguration
{
#region 成员字段
public static readonly string CacheKey = "ForumsConfiguration";
private Hashtable providers = new Hashtable();
private Hashtable extensions = new Hashtable();
private static readonly Cache cache = HttpRuntime.Cache;
private string defaultProvider;
private string defaultLanguage;
private string forumFilesPath;
private bool disableBackgroundThreads = false;
private bool disableIndexing = false;
private bool disableEmail = false;
private string passwordEncodingFormat = "unicode";
private int threadIntervalEmail = 15;
private int threadIntervalStats = 15;
//private SystemType systemType = SystemType.Self;
private short smtpServerConnectionLimit = -1;
private bool enableLatestVersionCheck = true;
private string uploadFilesPath = "/Upload/";
private XmlDocument XmlDoc = null;
#endregion
#region 构造器
public ForumConfiguration(XmlDocument doc)
{
XmlDoc = doc;
LoadValuesFromConfigurationXml();
}
#endregion
#region 获取XML节点
public XmlNode GetConfigSection(string nodePath)
{
return XmlDoc.SelectSingleNode(nodePath);
}
#endregion
/*
public static ForumConfiguration GetConfig()
{
return (ForumConfiguration) ConfigurationSettings.GetConfig("forums/forums");
}
*/
#region 获取配置信息实例
public static ForumConfiguration GetConfig()
{
ForumConfiguration config = cache.Get(CacheKey) as ForumConfiguration;
if(config == null)
{
string path;
if(HttpContext.Current != null)
path = HttpContext.Current.Server.MapPath("~/Forums.config");
else
path = Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + "Forums.config";
XmlDocument doc = new XmlDocument();
doc.Load(path);
config = new ForumConfiguration(doc);
cache.Insert(CacheKey,config,new CacheDependency(path),DateTime.MaxValue,TimeSpan.Zero,CacheItemPriority.AboveNormal,null);
//cache.ReSetFactor(config.CacheFactor);
}
return config;
}
#endregion
#region 加载配置文件设置
internal void LoadValuesFromConfigurationXml()
{
XmlNode node = GetConfigSection("Forums/Core");
XmlAttributeCollection attributeCollection = node.Attributes;
// 默认数据提供者
XmlAttribute att = attributeCollection["defaultProvider"];
if(att != null)
defaultProvider = att.Value;
// 默认语言
att = attributeCollection["defaultLanguage"];
if(att != null)
defaultLanguage =att.Value;
else
defaultLanguage = "zh-CN";
// 论坛路径
att = attributeCollection["forumFilesPath"];
if(att != null)
forumFilesPath = att.Value;
else
forumFilesPath = "/";
// 禁用后台线程
att = attributeCollection["disableThreading"];
if(att != null)
disableBackgroundThreads = bool.Parse(att.Value);
else
disableBackgroundThreads = false;
// 禁止索引
att = attributeCollection["disableIndexing"];
if(att != null)
disableIndexing = bool.Parse(att.Value);
else
disableIndexing = false;
// 禁止邮件
att = attributeCollection["disableEmail"];
if(att != null)
disableEmail = bool.Parse(att.Value);
else
disableEmail = false;
// 密码加密格式
att = attributeCollection["passwordEncodingFormat"];
if(att != null)
passwordEncodingFormat = att.Value;
else
passwordEncodingFormat = "unicode";
// 后中统计状态
att = attributeCollection["threadIntervalStats"];
if(att != null)
threadIntervalStats = int.Parse(att.Value);
else
threadIntervalStats = 15;
// 邮件队列发送间隔
att = attributeCollection["threadIntervalEmail"];
if(att != null)
threadIntervalEmail = int.Parse(att.Value);
else
threadIntervalEmail = 15;
// SMTP服务器设置
att = attributeCollection["smtpServerConnectionLimit"];
if(att != null)
smtpServerConnectionLimit = short.Parse(att.Value);
else
smtpServerConnectionLimit = -1;
// 版本检查
att = attributeCollection["enableLatestVersionCheck"];
if(att != null)
enableLatestVersionCheck = bool.Parse(att.Value);
// 上传文件路径
att = attributeCollection["uploadFilesPath"];
if(att != null)
uploadFilesPath = att.Value;
// 读取子节点
foreach (XmlNode child in node.ChildNodes)
{
if (child.Name == "providers")
GetProviders(child, providers);
if (child.Name == "extensionModules")
GetProviders(child, extensions);
}
}
#endregion
#region 获取配置文件节点Provider
internal void GetProviders(XmlNode node, Hashtable table)
{
foreach (XmlNode provider in node.ChildNodes)
{
switch (provider.Name)
{
case "add":
table.Add(provider.Attributes["name"].Value, new Provider(provider.Attributes));
break;
case "remove":
table.Remove(provider.Attributes["name"].Value);
break;
case "clear":
table.Clear();
break;
}
}
}
#endregion
#region 公有属性
// Properties
//
public string DefaultLanguage
{
get { return defaultLanguage; }
}
public string ForumFilesPath
{
get { return forumFilesPath; }
}
public string DefaultProvider
{
get { return defaultProvider; }
}
public Hashtable Providers
{
get { return providers; }
}
public Hashtable Extensions
{
get { return extensions; }
}
public bool IsBackgroundThreadingDisabled
{
get { return disableBackgroundThreads; }
}
public bool IsIndexingDisabled
{
get { return disableIndexing; }
}
public string PasswordEncodingFormat
{
get { return passwordEncodingFormat; }
}
public string UploadFilesPath
{
get { return uploadFilesPath; }
}
public bool IsEmailDisabled
{
get { return disableEmail; }
}
public int ThreadIntervalEmail
{
get { return threadIntervalEmail; }
}
public int ThreadIntervalStats
{
get { return threadIntervalStats; }
}
public short SmtpServerConnectionLimit
{
get { return smtpServerConnectionLimit; }
}
public bool EnableLatestVersionCheck
{
get { return enableLatestVersionCheck; }
}
#endregion
}
/// <summary>
/// 数据提供者实体类
/// </summary>
public class Provider
{
#region 成员字段
private string name;
private string providerType;
private ExtensionType extensionType;
private NameValueCollection providerAttributes = new NameValueCollection();
#endregion
#region 构造器
public Provider(XmlAttributeCollection attributes)
{
// Set the name of the provider
//
name = attributes["name"].Value;
// Set the extension type
//
try
{
extensionType = (ExtensionType)Enum.Parse(typeof(ExtensionType), attributes["extensionType"].Value, true);
}
catch
{
// Occassionally get an exception on parsing the extensiontype, so set it to Unknown
extensionType = ExtensionType.Unknown;
}
providerType = attributes["type"].Value;
// Store all the attributes in the attributes bucket
//
foreach (XmlAttribute attribute in attributes)
{
if ((attribute.Name != "name") && (attribute.Name != "type"))
providerAttributes.Add(attribute.Name, attribute.Value);
}
}
#endregion
#region 公有属性
public string Name
{
get { return name; }
}
public string Type
{
get { return providerType; }
}
public ExtensionType ExtensionType
{
get { return extensionType; }
}
public NameValueCollection Attributes
{
get { return providerAttributes; }
}
#endregion
}
}
// *********************************************************************
// ForumsConfigurationHandler
//
/// <summary>
/// Class used by ASP.NET Configuration to load ASP.NET Forums configuration.
/// </summary>
///
// ***********************************************************************/
/*
internal class ForumsConfigurationHandler : IConfigurationSectionHandler
{
public virtual object Create(Object parent, Object context, XmlNode node)
{
ForumConfiguration config = new ForumConfiguration();
config.LoadValuesFromConfigurationXml(node);
return config;
}
}
*/
这个配置文件配置了ANF的默认数据提供者、默认语言、论坛路径、是滞禁用后台线程、是滞禁止索引、是否禁止邮件、密码加密格式、线程间隔状态、邮件队列发送间隔、SMTP服务器设置、版本检查
上传文件路径、数据提供者、自定义HttpModule等。主要方法为GetConfig():
public static ForumConfiguration GetConfig()
{
ForumConfiguration config = cache.Get(CacheKey) as ForumConfiguration;
if(config == null)
{
string path;
if(HttpContext.Current != null)
path = HttpContext.Current.Server.MapPath("~/Forums.config");
else
path = Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + "Forums.config";
XmlDocument doc = new XmlDocument();
doc.Load(path);
config = new ForumConfiguration(doc);
cache.Insert(CacheKey,config,new CacheDependency(path),DateTime.MaxValue,TimeSpan.Zero,CacheItemPriority.AboveNormal,null);
//cache.ReSetFactor(config.CacheFactor);
}
return config;
}
在此方法中,首先从Cache缓存中获取配置信息,如配置信息还未缓存,则读敢配置文件。配置文件位于网站根目录下的Forums.config文件。读取配置文件的内容后插入缓存中以便下次使用。
Forums.config文件如下:
<?xml version="1.0" encoding="utf-8" ?>
<Forums>
<Core defaultLanguage="zh-CN"
forumFilesPath="/"
disableEmail="false"
disableIndexing="true"
disableThreading="false"
threadIntervalStats="15"
threadIntervalEmail="15"
smtpServerConnectionLimit="-1"
passwordEncodingFormat="unicode"
enableLatestVersionCheck="false"
uploadFilesPath="/Upload/"
defaultProvider="SqlForumsProvider">
<providers>
<clear />
<add name="LocalSqlForumsProvider" type="AspNetForums.Data.SqlDataProvider, AspNetForums.SqlDataProvider"
connectionString="server=.;database=Forums;uid=sa;pwd=" databaseOwner="dbo" />
<add name="SqlForumsProvider" type="AspNetForums.Data.SqlDataProvider, AspNetForums.SqlDataProvider"
connectionStringName = "SiteSqlServer" databaseOwnerStringName = "SiteSqlServerOwner" />
<add name="SqlDataProviderNonDbo" type="AspNetForums.Data.SqlDataProviderNonDbo, AspNetForums.SqlDataProviderNonDbo"
connectionStringName = "SiteSqlServer" databaseOwnerStringName = "SiteSqlServerOwner" />
</providers>
<extensionModules>
<add name="PassportAuthentication" extensionType="Security" type="Telligent.CommunityServer.Security.PassportAuthentication, Telligent.CommunityServer.SecurityModules" />
<add name="WindowsAuthentication" extensionType="Security" type="Telligent.CommunityServer.Security.WindowsAuthentication, Telligent.CommunityServer.SecurityModules"
allowAutoUserRegistration="true" adminWindowsGroup="Administrators" adminWindowsGroupIsSystemAdministrator="true" />
<add name="FormsAuthentication" extensionType="Security" type="Telligent.CommunityServer.Security.FormsAuthentication, Telligent.CommunityServer.SecurityModules"
allowAutoUserRegistration="true" userEmailAddressCookie="CSUserEmailAddress" useEncryptedEmailAddressCookie="false" />
</extensionModules>
</Core>
</Forums>
二、缓存
通过在类中添加private static readonly Cache cache = HttpRuntime.Cache;来实现。
三、多数据访问
ANF的数据访问基类是ForumsDataProvider类,ForumsDataProvider这个类位于AspNetForums.Components命名空间下。这是一个抽象类,你可以去实现你的SQL子类或其他子类。主要方法为两个实例化方法。
实例化方法一:
public static ForumsDataProvider Instance()
{
// 理论上应该出现不了null,但程序运行确实出现过.原因待查.
if(_defaultInstance == null)
CreateDefaultCommonProvider();
return _defaultInstance;
}
在第一个实例化方法中,使用了单例设计模式,如果默认实例为空,则创建默认实例,方法如下:
private static void CreateDefaultCommonProvider()
{
ForumConfiguration config = ForumConfiguration.GetConfig();
Provider sqlForumsProvider = (Provider) config.Providers[config.DefaultProvider];
_defaultInstance = DataProviders.CreateInstance(sqlForumsProvider) as ForumsDataProvider;
}
首先获取配置信息,通过配置信息中的默认数据提供者来创建默认实例。
实例化方法二:
public static ForumsDataProvider Instance (Provider dataProvider)
{
ForumsDataProvider fdp = cache.Get(dataProvider.Name) as ForumsDataProvider;
if(fdp == null)
{
fdp = DataProviders.Invoke(dataProvider) as ForumsDataProvider;
cache.Insert(dataProvider.Name,fdp,null,DateTime.MaxValue,TimeSpan.Zero,CacheItemPriority.AboveNormal,null);
}
return fdp;
}
在第二个实例化方法中,使用了缓存,通过给定的提供者,如果缓存中有,则直接调用。如果没有,则创建。创建后插入缓存以便下次使用。
其它抽象方法为子类需要实现的真正的数据访问方法。
欢迎大家指正。