相关链接:
Discuz!NT代码阅读笔记(1)--从HttpModule开始
Discuz!NT代码阅读笔记(2)--网站安装也能自动化:论坛程序安装及初始化过程
前文提到:
在DNT安装项目:Discuz.Install中,有9个CS文件。其中位于SetupPage.cs.文件中的SetupPage类是继承自System.Web.UI.Page。它是其安装过程中用到的其他页面类的基类。
在SetupPage的构造函数中,程序组织了一些版权、版本等文本的Html。但在整个安装过程中,我没看见这些信息。
安装时,由于Index.Aspx没有隐藏任何类的信息,可以当作静态页面来看待。
安装过程中的第二和第三个页面完成信息收集工作,之后在第四个页面自信数据库脚本创建存储过程和表。前两个页面基层之文件step3.aspx.cs中的install类,最后一个页面基层自step4.aspx.cs文件中的InstallStep4类。这篇就说说安装过程中用到的几个有意思函数。
1、安装前软件BIN目录检测函数IISSystemBINCheck()
这个函数位于SetupPage类中,检查程序依赖的DLL是否都存在。通过File.Exists()判断文件是否存在。
2、检查对程序目录的读写权限函数SystemFolderCheck()
这个函数通过在程序主目录下创建一个文件,之后再删除这个文件来检查对程序根目录的读写权限
3、检查对操作系统临时目录的读写权限函数TempTest()
private static bool TempTest()
{
string UserGuid = Guid.NewGuid().ToString();
string TempPath = Path.GetTempPath();
string path = TempPath + UserGuid;
try
{
using (StreamWriter sw = new StreamWriter(path))
{
sw.WriteLine(DateTime.Now);
}
using (StreamReader sr = new StreamReader(path))
{
sr.ReadLine();
return true;
}
}
catch
{
return false;
}
}
这个函数首先通过System.IO中的Path.GetTempPath();函数获取系统历史文件目录,之后使用Guid.NewGuid().ToString()在临时目录下随机生成一个文件目录名并创建一个文件,在文件中追加一行数据并读取,以测试对临时目录的权限。
4、配置文件编辑函数EditDntConfig()
protected void EditDntConfig(string dataSource, string userID, string password, string databaseName, string tablePrefix)
{
BaseConfigInfo baseConfig = BaseConfigs.GetBaseConfig();
connectionString = string.Format(@"Data Source={0};User ID={1};Password={2};Initial Catalog={3};Pooling=true",
dataSource, userID, password, databaseName);
baseConfig.Dbconnectstring = connectionString;
baseConfig.Tableprefix = tablePrefix;
baseConfig.Dbtype = "SqlServer";
string dntPath = Utils.GetMapPath("~/DNT.config");
if (!Utils.FileExists(dntPath))
{
dntPath = Utils.GetMapPath("/DNT.config");
}
SerializationHelper.Save(baseConfig, dntPath);
DbHelper.ConnectionString = baseConfig.Dbconnectstring;
BaseConfigs.ResetRealConfig();
}
编辑网站根目录下DNT.Config文件使用。通过传入的参数,组织了连接字符串即:
connectionString = string.Format(@"Data Source={0};User ID={1};Password={2};Initial Catalog={3};Pooling=true",
dataSource, userID, password, databaseName);
然后将这个字符串保存到网站目录下的DNT.Config文件中。
5、检测数据库是否存在函数CheckDatabaseExists()
protected int CheckDatabaseExists()
{
int result = 0;
try
{
connectionString = string.Format(@"Data Source={0};User ID={1};Password={2};Initial Catalog={3};Pooling=false",
Request.QueryString["sql_ip"], Request.QueryString["sql_username"], Request.QueryString["sql_password"], Request.QueryString["sql_name"]);
commandText = string.Format("SELECT DB_ID('{0}')", Request.QueryString["sql_name"]);
result = ExcuteReader(commandText, connectionString);
//EditDntConfig(Request["sql_ip"], Request["sql_username"], Request["sql_password"], Request["sql_name"], Request["table_prefix"]);//执行成功后,将数据库连接信息写入DNT.config文件
}
catch (Exception)
{
Response.Write("{\"Result\":false,\"Message\":\"数据库不存在!请重新填写;或者选择自动创建数据库\"}");
Response.End();
return result;
}
if (result == 0)
{
Response.Write("{\"Result\":false,\"Message\":\"数据库不存在!请重新填写;或者选择自动创建数据库\"}");
Response.End();
return result;
}
else
{
Response.Write("{\"Result\":true,\"Message\":true}");
Response.End();
return result;
}
}
这个函数通过try{} catch{} 测试是否能连接到数据库。在Try中,根据用户输入信息组织一条语句,并在数据库中执行。如果能够执行,说明数据库连接正常。否则抛出异常,有Catch完成异常信息的显示和输出。
6、读取数据库脚本并执行的函数:CreateTableAndSP()
/// <summary>
/// 建表和存储过程
/// </summary>
private void CreateTableAndSP()
{
tableprefix = BaseConfigs.GetTablePrefix;
#region 建表
StringBuilder sb = new StringBuilder();
using (StreamReader objReader = new StreamReader(Server.MapPath(dbScriptPath + "setup2.1.sql"), Encoding.UTF8))
{
sb.Append(objReader.ReadToEnd());
objReader.Close();
}
if (tableprefix.ToLower() == "dnt_")
{
DbHelper.ExecuteCommandWithSplitter(sb.ToString());
}
else
{
DbHelper.ExecuteCommandWithSplitter(sb.ToString().Replace("dnt_", tableprefix));
//DbHelper.ExecuteNonQuery(sb.ToString().Replace("dnt_",tableprefix));
}
#endregion
#region 建存储过程
sb.Remove(0, sb.Length); //将StringBuild内容清空
sqlServerVersion = DbHelper.ExecuteScalar(CommandType.Text, "SELECT @@VERSION").ToString().Substring(20, 24).Trim();
if (sqlServerVersion.IndexOf("2000") >= 0)
{
using (StreamReader objReader = new StreamReader(Server.MapPath(dbScriptPath + "setup2.2.sql"), Encoding.UTF8))
{
sb.Append(objReader.ReadToEnd());
objReader.Close();
}
}
else
{
using (StreamReader objReader = new StreamReader(Server.MapPath(dbScriptPath + "setup2.2 - 2005.sql"), Encoding.UTF8))
{
sb.Append(objReader.ReadToEnd());
objReader.Close();
}
}
if (tableprefix.ToLower() == "dnt_")
{
DbHelper.ExecuteCommandWithSplitter(sb.ToString().Trim().Replace("\"", "'"));
}
else
{
DbHelper.ExecuteCommandWithSplitter(sb.ToString().Trim().Replace("\"", "'").Replace("dnt_", tableprefix));
}
#endregion
}
这个函数中首先通过StreamReader读取数据库脚本,接下类根据用户输入的初始化表头前缀替换掉数据库脚本中的表盒存储过程的前缀dnt_。
最后通过DBHelper类中执行脚本的函数ExecuteCommandWithSplitter()完成数据库和表的创建工作。
数据库内容初始化函数跟这个相似,也是读脚本-执行的过程。
7、代码中使用了很多DBHelper类和Utils类中的函数。
这个类是数据库操作类和配置类。在这里使用起来很方便,省去了很多数据库的初始化和连接的配置工作。这里可以看出分层和OOP的好处:写一个函数,大家调用。
8、一点点体会:
整个论坛程序的安装其实就是执行一些环境检测和数据库的初始化。环境检测主要是通过在需要检测的地方创建一个文件,向文件中些数据后读取这些数据,最后删除创建的文件这几个步骤来进行的。数据库检测是通过在try{ }。。。catch{ }。。。段中打开连接-执行一些操作来判断的。如果没有抛出异常,说明数据库正常,否则就是连接恶补正确。
DNT作了个这样的东西,虽然不大,但很方便。即使不懂ASPX的人也能看着帮助完成数据库程序的安装。所有安装操作和配置都在浏览器中进行,不用手动去改配置文件。
完善的数据库操作类用处非常大,可以省去很多代码。我之前写过一个类,每次用到数据库操作,都要新建一个诸如Command、Connection、SqlDataReader、SqlDataAdapter之类的对象,然后组织 Sql语句,一个个地写,麻烦大大地有。
这篇比较简单,有理解不透或语言组织不足之处还望大家指正。
DNT程序后台配置功能很强大,基本上可以达到一切皆可配置的地步。这种配置跟他的页面组织有很大关系。下一篇我先写写DNT如何处理页面显示的。敬请关注。