在运用Visiol studio.NET 开发Web 应用程式中,开发者常常会遇到一些问题:如我开发好的程式,在开发环境下测试没问题,怎么一搬到应用环境下,就会有问题? 不是程式的无法运行,就是程式的效率慢的同蜗牛在爬, 这种情况在.NET的新手中尤其常见。我不知道为什么,一些介绍.NET开发的书本里引用的例子代码,也对此问题视而不见,尤其让我郁闷的是一些我喜欢的书,如:<<ADO.NET技术内幕>>,<<ASP.NET2.0高级编程(第4版 )>>,这两本都是清华大学出版社出版的, 有一本书更糟糕<<Visual Basic.Net 专业项目实例开发>>,我建议大家还是不要看了吧,免的浪费时间和精力。
这篇文章不仅对.NET开发者的新手有帮助,同样对哪些有经验,也带来一些启示和参考。
他们会遇到什么样的问题,我不妨总结给大家:
1. 数据库连接超时
2. 创建的对象只管用,不管释放
3. 调试(Debug)模式下编译后,就用于应用环境中了
4. 实际作业模式分享
上面的问题就像毒瘤,积累到一定程度就爆发,且影响深远。
一、数据库连接超时篇
若要知道数据库连接超时问题,先看下面一段代码:
[Sample-01]:
Public Shared Function getOEMPN(ByVal psPN As String, ByRef OEMPN As String) As BSResult
0001 Dim clsResult As New BSResult
0002 Try
0003 clsResult.ResultID = -1
0004 Dim dtResult As New DataTable
0005 Dim Sql As String = String.Empty
0006 Dim clsOraDb As New clsOraClienDb
0007 Dim strConn As String = ConfigurationManager.ConnectionStrings("ConnectionString").ConnectionString
0008 clsOraDb.Open(strConn) ‘这里Open后,后面看不到 clsOraDb.Close
0009 Sql = "SELECT SATBMMBRND.OEMPN FRUNO FROM SATBMMBRND WHERE SATBMMBRND.MATNO = :MATNO"
0010 Dim params() As OracleParameter = {New OracleParameter("MATNO", psPN)}
0011 If clsOraDb.FillDataTable(Sql, dtResult, params) = False Then
0012 Return clsResult
0013 End If
0014 If dtResult Is Nothing Then
0015 Return clsResult
0016 End If
0017 If dtResult.Rows.Count > 0 Then
0018 OEMPN = dtResult.Rows(0)("FRUNO").ToString()
0019 Else
0020 OEMPN = ""
0021 End If
0022 clsResult.ResultID = 1
0023 Return clsResult
0024 Catch ex As Exception
0025 clsResult.ResultID = -1
0026 Return clsResult
0027 End Try
End Function
对上述代码行的部分解释:
0006:引用数据库连接的类;
0008:打开数据库连接;
然后,整个函数你再找不到关闭数据库连接的动作,是要等着操作系统来释放吗? 有人就说啦,看起来好像没有什么大不了的,这仅仅是一个函数而已;数据库打开连接,未关闭不会影响到整个应用程式;果真是这样吗?
让我们谈谈数据库连接的问题,在Oracle数据库里,一般默认的数据库连接数最多也就100多来个,不会超过200个,即使你改变这个连接数,但无论怎样,它的连接数是有限的,不可能无限地供你消耗。
在Web这个程式里,它不仅不会自动关闭数据库连接,象这样的函数还会每次调用,都会重新用掉一个数据库连接;如果象这样的函数很多的话,你就等着一个错误警告页面弹出来,如Database Connection Timeout….等讯息。
这还不算什么,更有甚者,尽然在循环语句里写下面的代码如 :
[Sample-02]
Foreach(DataRow row in tabl.select(“”,”ProductID”)
……………
clsOraDb.Open(strConn)
………….
Next
有人还喜欢玩下面的语句:
[Sample-03]
Foreach(DataRow row in tabl.select(“”,”ProductID”)
Foreach(DataColumn col in tbl.columns)
……………
clsOraDb.Open(strConn)
Next
………….
Next
说到这,有人就问啦,我在开发环境下测试一点问题都没有呀?是呀,你是没有问题,我想问的是,你开发环境的测试数据有几笔?
现在,问题已经知道在哪里,怎么解决?
针对[sample-01]做如下处理,注意下面代码:
Public Shared Function getOEMPN(ByVal psPN As String, ByRef OEMPN As String) As BSResult
0001 Dim clsResult As New BSResult
0002 Try
0003 clsResult.ResultID = -1
0004 Dim dtResult As New DataTable
0005 Dim Sql As String = String.Empty
0006 Dim clsOraDb As New clsOraClienDb
0007 Dim strConn As String = ConfigurationManager.ConnectionStrings("ConnectionString").ConnectionString
0008 clsOraDb.Open(strConn) 注释:这里Open后,后面看不到 clsOraDb.Close
0009 Sql = "SELECT SATBMMBRND.OEMPN FRUNO FROM SATBMMBRND WHERE SATBMMBRND.MATNO = :MATNO"
0010 Dim params() As OracleParameter = {New OracleParameter("MATNO", psPN)}
0011 If clsOraDb.FillDataTable(Sql, dtResult, params) = False Then
0012 Return clsResult
0013 End If
0014 If dtResult Is Nothing Then
0015 Return clsResult
0016 End If
0017 If dtResult.Rows.Count > 0 Then
0018 OEMPN = dtResult.Rows(0)("FRUNO").ToString()
0019 Else
0020 OEMPN = ""
0021 End If
0022 clsResult.ResultID = 1
0088 clsOraDb.Close 注释:后面看到 clsOraDb.Close
0023 Return clsResult
0024 Catch ex As Exception
0099 clsOraDb.Close 注释:程序异常也看到 clsOraDb.Close
0025 clsResult.ResultID = -1
0026 Return clsResult
0028 Throw ex
0027 End Try
End Function
注意上面的两句代码:0088行和0099行。
在异常处理的时候,特别提醒两点:
一, 你的数据库关闭的时候应该是在代码行0028前,而不是后;
二, 有人不习惯(或者一时疏忽)加上0088行的代码;
针对[Sample-02]和[sample-03],把打开数据库连接写在所有的循环语句之前,如:
clsOraDb.Open(strConn)
Foreach(DataRow row in tabl.select(“”,”ProductID”)
……………
………….
Next
当然还有另外一个做法,就是用Using语句,提交.NET应用的垃圾收集器自动收集;相关的文章很多,这里不再特别赘述。
二、对象只管创建应用,不管释放篇
我们继续用[Sample-01]的代码,我们现在看0004行的代码:
0004 Dim dtResult As New DataTable
谁会发现它被释放,你不能,我也不能,从来没有被释放过。
“0004”行的代码解释是,要在内存划分一个空间给这个定义的对象dtresult;系统要划分多大的空间呢?呀,我没有研究过(留给那些有心人吧,呵呵..)。但有一点,要在内存划分一个空间,就是要占用内存。那么内存有多大呢,不是无限大吧,也是有限的,所有运行上述代码的最终结果是,系统的执行效率越来越慢,有人就怀疑,我有内存1到2G的,加上虚拟内存就更大,我只能说你的怀疑没错。可是你的应用程序就用这么一只函数吗?我想肯定不是,所以上百只函数的应用执行对内存的消耗可想而知。如果是后台自动运行的程序,及时是一个function,也会让系统崩溃。这只是一个简单的例子,有更复杂的。像这样的对象应用还有:Dataset, Datatable,DataReader,DataAdapter,Datagrid..等。
那么怎么解决这些问题呢:
2.1在Try catch 语句前定义好所用的对象, 如:
Dim dtResult As New DataTable
Dim DR as New DataReader
Dim DS as New Dataset
Try
..
Catch ex As Exception
Throw ex
Finally
End Try
2.2释放的语句如下
Dim dtResult As New DataTable
Dim DR as New DataReader
Dim DS as New Dataset
Try
..
……………..
Catch ex As Exception
--释放应用的对象
Throw ex
Finally
--使用完后,释放应用的对象
dtResult.dispose --从内存里清楚该对象
DR.dispose -从内存里清楚该对象
DS.dispose -从内存里清楚该对象
End Try
有人习惯写成下面这样:
Dim dtResult As New DataTable
Dim DR as New DataReader
Dim DS as New Dataset
Try
..
‘使用完后,释放应用的对象
dtResult.dispose ‘从内存里清楚该对象
DR.dispose ‘从内存里清楚该对象
DS.dispose ‘从内存里清楚该对象
Catch ex As Exception
‘释放应用的对象
Throw ex
Finally
End Try
这不是也释放了吗?我想问的是,如果程序出现异常,它们会释放吗 ?我肯定得告诉大家,它们一定不能释放,为了确保程序的稳定运行,我建议大家都来用Try Catch语句。