PDF.NET(PWMIS数据开发框架)是一个基于SQL-MAP(将SQL语句映射成数据访问代码)和OQL(对象化的SQL查询表达式)技术的数据开发框架,兼有SQL和ORM的特点。尽管有些数据库系统能够提供SQL监视,比如SQLSERVER的事务日志跟踪,但好多数据库都没有提供这样的功能,所以很多数据(或者ORM)框架都会有一个记录执行的SQL日志的功能,PDF.NET也不例外,下面讲讲它是怎么实现的。
1,SQL日志功能代码介绍
PDF.NET 底层访问数据库使用的是兼容MS DAAB 3.1的AdoHelper,它在最终执行Command命令查询的时候,调用了 CommandLog对象,而这个类内部有一个Stopwatch对象,用于精确记录执行时间。
private
System.Diagnostics.Stopwatch watch
=
null
;
///
<summary>
///
是否开启执行时间记录
///
</summary>
///
<param name="startStopwatch"></param>
public
CommandLog(
bool
startStopwatch)
{
if
(startStopwatch)
{
watch
=
new
System.Diagnostics.Stopwatch();
watch.Start();
}
}
CommandLog类公开一个WriteLog方法,记录Ado.Net的Command对象的执行情况:
///
<summary>
///
写命令日志和执行时间(如果开启的话)
///
</summary>
///
<param name="command">
命令对象
</param>
///
<param name="who">
调用命令的源名称
</param>
public
void
WriteLog(IDbCommand command,
string
who)
{
CommandText
=
command.CommandText;
if
(SaveCommandLog)
{
if
(watch
!=
null
)
{
if
((LogExecutedTime
>
0
&&
watch.ElapsedMilliseconds
>
LogExecutedTime)
||
LogExecutedTime
==
0
)
{
RecordCommandLog(command, who);
WriteLog(
"
Execueted Time(ms):
"
+
watch.ElapsedMilliseconds
+
"
\r\n
"
, who);
}
}
else
{
RecordCommandLog(command, who);
}
}
}
在CommandDB类(AdoHelper的基类)的相关数据访问方法内,如下调用CommandLog类:
///
<summary>
///
根据查询返回数据阅读器对象
///
</summary>
///
<param name="SQL">
SQL
</param>
///
<param name="commandType">
命令类型
</param>
///
<param name="cmdBehavior">
对查询和返回结果有影响的说明
</param>
///
<param name="parameters">
参数数组
</param>
///
<returns>
数据阅读器
</returns>
protected
virtual
IDataReader ExecuteDataReader(
ref
string
SQL, CommandType commandType, CommandBehavior cmdBehavior,
ref
IDataParameter[] parameters)
{
IDbConnection conn
=
GetConnection();
IDbCommand cmd
=
conn.CreateCommand ();
CompleteCommand(cmd,
ref
SQL,
ref
commandType,
ref
parameters);
CommandLog
cmdLog
=
new
CommandLog(
true
);
IDataReader reader
=
null
;
try
{
//
如果命令对象的事务对象为空,那么强制在读取完数据后关闭阅读器的数据库连接 2008.3.20
if
(cmd.Transaction
==
null
&&
cmdBehavior
==
CommandBehavior.Default )
cmdBehavior
=
CommandBehavior.CloseConnection ;
reader
=
cmd.ExecuteReader (cmdBehavior);
}
catch
(Exception ex)
{
ErrorMessage
=
ex.Message ;
//
只有出现了错误而且没有开启事务,可以关闭连结
if
(cmd.Transaction
==
null
&&
conn.State
==
ConnectionState.Open )
conn.Close ();
bool
inTransaction
=
cmd.Transaction
==
null
?
false
:
true
;
CommandLog.Instance.WriteErrLog(cmd,
"
AdoHelper:
"
+
ErrorMessage);
if
(OnErrorThrow)
{
throw
new
QueryException(ex.Message, cmd.CommandText, commandType, parameters, inTransaction, conn.ConnectionString);
}
}
cmdLog
.WriteLog(cmd,
"
AdoHelper
"
);
return
reader;
}
2,SQL日志功能使用配置
使用SQL日志很简单,只需要在应用程序配置文件中做如下配置即可,注意看配置中的注释。
<!--
PDF.NET SQL 日志记录配置(for 4.0)开始
记录执行的SQL语句,关闭此功能请将SaveCommandLog 设置为False,或者设置DataLogFile 为空;
如果DataLogFile 的路径中包括~符号,表示SQL日志路径为当前Web应用程序的根目录;
如果DataLogFile 不为空且为有效的路径,当系统执行SQL出现了错误,即使SaveCommandLog 设置为False,会且仅仅记录出错的这些SQL语句;
如果DataLogFile 不为空且为有效的路径,且SaveCommandLog 设置为True,则会记录所有的SQL查询。
在正式生产环境中,如果不需要调试系统,请将SaveCommandLog 设置为False 。
-->
<
add
key
="SaveCommandLog"
value
="True"
/>
<
add
key
="DataLogFile"
value
="~\SqlLog.txt"
/>
<!--
LogExecutedTime 需要记录的时间,如果该值等于0会记录所有查询,否则只记录大于该时间的查询。单位毫秒。
-->
<
add
key
="LogExecutedTime"
value
="300"
/>
<!--
PDF.NET SQL 日志记录配置 结束
-->
注意:日志路径可以使用ASP.NET的服务器路径符号“~”,该符号的具体使用说明是:
ASP.NET 包括了 Web 应用程序根目录运算符 (~),当您在服务器控件中指定路径时可以使用该运算符。ASP.NET 会将 ~ 运算符解析为当前应用程序的根目录。可以结合使用 ~ 运算符和文件夹来指定基于当前根目录的路径。
下面的示例演示了使用 Image 服务器控件时用于为图像指定根目录相对路径的 ~ 运算符。在此示例中,无论页面位于网站中的什么位置,都将从位于 Web 应用程序根目录下的 Images 文件夹中直接读取图像文件。
<asp:image runat="server" id="Image1"
ImageUrl="~/Images/SampleImage.jpg" />可以在服务器控件中的任何与路径有关的属性中使用 ~ 运算符。~ 运算符只能为服务器控件识别,并且位于服务器代码中。不能将 ~ 运算符用于客户端元素。
详细内容请看
3,查看SQL日志文件
根据配置文件中配置的SQL日志地址,我们查看一下它的内容,看它到底记录了什么内容:
//
2011
/
5
/
9
14
:
48
:
42
@AdoHelper 执行命令:
SQL
=
"
SELECT * FROM [JJGaiKuang] (@fundCompany,@fundType,@IsConsignment,@managerID,@openState,@bankName,@Tzfg)
"
//命令类型:Text
//7个命令参数:
Parameter
[
"@fundCompany"
]
=
""
//DbType
=
AnsiString
Parameter
[
"@fundType"
]
=
""
//DbType
=
AnsiString
Parameter
[
"@IsConsignment"
]
=
"
是
"
//DbType
=
AnsiString
Parameter
[
"@managerID"
]
=
""
//DbType
=
AnsiString
Parameter
[
"@openState"
]
=
""
//DbType
=
AnsiString
Parameter
[
"@bankName"
]
=
"
中国银行
"
//DbType
=
AnsiString
Parameter
[
"@Tzfg"
]
=
""
//DbType
=
AnsiString
//
2011
/
5
/
9
14
:
48
:
42
@AdoHelper :Execueted Time(ms):
607
//
2011
/
5
/
9
14
:
48
:
59
@AdoHelper 执行命令:
SQL
=
"
SELECT * FROM [GetFundTrend_FundAnalysis_FundFeat] (@currentJJDM,@OtherJJDM)
"
//命令类型:Text
//2个命令参数:
Parameter
[
"@currentJJDM"
]
=
"
KF0003
"
//DbType
=
AnsiString
Parameter
[
"@OtherJJDM"
]
=
"
000001,399001,H11020,000300
"
//DbType
=
AnsiString
//
2011
/
5
/
9
14
:
48
:
59
@AdoHelper :Execueted Time(ms):
369
//
2011
/
5
/
9
14
:
49
:
00
@AdoHelper 执行命令:
SQL
=
"
SELECT a.id,a.基金名称,round(a.收益率*100,2) 收益率 FROM [GetFundOfTypeNew](@jjdm,@startDate,@endDate,@type) as a
"
//命令类型:Text
//4个命令参数:
Parameter
[
"@jjdm"
]
=
"
KF0003
"
//DbType
=
AnsiString
Parameter
[
"@startDate"
]
=
"
2011-02-06
"
//DbType
=
AnsiString
Parameter
[
"@endDate"
]
=
"
2011-05-06
"
//DbType
=
AnsiString
Parameter
[
"@type"
]
=
"
三个月
"
//DbType
=
AnsiString
//
2011
/
5
/
9
14
:
49
:
00
@AdoHelper :Execueted Time(ms):
310
//
2011
/
5
/
9
14
:
49
:
00
@AdoHelper 执行命令:
SQL
=
"
SELECT * FROM [GetFundNotice](@jjdm) order by 公告时间 desc
"
//命令类型:Text
//1个命令参数:
Parameter
[
"@jjdm"
]
=
"
KF0003
"
//DbType
=
AnsiString
//
2011
/
5
/
9
14
:
49
:
00
@AdoHelper :Execueted Time(ms):
389
从日志文件可以看出,程序记录了详细的SQL信息,包括SQL文本和参数值,还有执行时间,本示例文件中仅仅记录了执行超过300毫秒的查询。
通过框架的SQL日志功能,可以随时打开或者关闭日志,查看日志详细信息,从而为系统性能优化提供依据。
注:本功能的完全支持仅在框架版本 4.1之后,之前的版本不支持 LogExecutedTime 功能和“~”路径功能。