「.NET」と「ASP.NET」からの接続には大きな違いがあります。
それは普通の?(exe形式の)アプリケーションか、Webアプリケーションかです。
この違いはアプリの終了というタイミングに大きく影響してきます。
「.NET」の場合は、例えどれだけコネクションがあろうとも、exeが終了した段階でコネクションはCloseされます。
更に正確にいうと、exeが終了した段階で、Close処理を実施していないコネクションも、ガベージコレクションによりCloseされます。
定期的に実行されるexeでは、もはやclose処理を意識しなくてもいいかもしれません。
このことは下記リンクのプログラムで確認することができます。
ポイントは2つ。app.configと、コメントアウトです。
肝心なNumberOfPooledConnectionsを確認するには下記設定が必用です。
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
</startup>
<system.diagnostics>
<switches>
<add name="ConnectionPoolPerformanceCounterDetail" value="4"/>
</switches>
</system.diagnostics>
</configuration>
またサンプルでは、重要な個所がコメントアウトされているので注意しましょう。
Private Enum ADO_Net_Performance_Counters
NumberOfActiveConnectionPools
NumberOfReclaimedConnections
HardConnectsPerSecond
HardDisconnectsPerSecond
NumberOfActiveConnectionPoolGroups
NumberOfInactiveConnectionPoolGroups
NumberOfInactiveConnectionPools
NumberOfNonPooledConnections
NumberOfPooledConnections
NumberOfStasisConnections
' The following performance counters are more expensive to track.
' Enable ConnectionPoolPerformanceCounterDetail in your config file.
' SoftConnectsPerSecond
' SoftDisconnectsPerSecond
' NumberOfActiveConnections
' NumberOfFreeConnections
End Enum
多くの人の感心は「ASP.NET」でのコネクションと解放でしょう。
同じプログラムを流用して、.NETのコネクションも確認することができます。
私は下記サンプルを使用して何度も試行しました。
Default.aspx
Imports System.Data.SqlClient
Imports System.Diagnostics
Imports System.Runtime.InteropServices
Partial Class _Default
Inherits System.Web.UI.Page
Private con As New SqlConnection
Private PerfCounters(13) As PerformanceCounter
Private connection As SqlConnection = New SqlConnection
Private Enum ADO_Net_Performance_Counters
NumberOfActiveConnectionPools
NumberOfReclaimedConnections
HardConnectsPerSecond
HardDisconnectsPerSecond
NumberOfActiveConnectionPoolGroups
NumberOfInactiveConnectionPoolGroups
NumberOfInactiveConnectionPools
NumberOfNonPooledConnections
NumberOfPooledConnections
NumberOfStasisConnections
' The following performance counters are more expensive to track.
' Enable ConnectionPoolPerformanceCounterDetail in your config file.
SoftConnectsPerSecond
SoftDisconnectsPerSecond
NumberOfActiveConnections
NumberOfFreeConnections
End Enum
Protected Sub Page_Load(sender As Object, e As EventArgs) Handles Me.Load
con.ConnectionString = "Data Source=localhost;User Id=user;Password=pass;Initial Catalog=TestDB;Pooling=true;Min Pool Size=0;Max Pool Size=10;Connection Lifetime=1"
con.Open()
System.Threading.Thread.Sleep(5000)
con.Close()
Me.PerfCounters(13) = New PerformanceCounter()
Dim instanceName As String = GetInstanceName()
Dim apc As Type = GetType(ADO_Net_Performance_Counters)
Dim i As Integer = 0
Dim s As String = ""
For Each s In [Enum].GetNames(apc)
Me.PerfCounters(i) = New PerformanceCounter()
Me.PerfCounters(i).CategoryName = ".NET Data Provider for SqlServer"
Me.PerfCounters(i).CounterName = s
Me.PerfCounters(i).InstanceName = instanceName
i = (i + 1)
Next
Response.Write("---------------------------" & "<br/>")
For Each p As PerformanceCounter In Me.PerfCounters
Response.Write(p.CounterName & ":" & p.NextValue & "<br/>")
Next
Response.Write("---------------------------" & "<br/>")
End Sub
Private Declare Function GetCurrentProcessId Lib "kernel32.dll" () As Integer
Private Function GetInstanceName() As String
'This works for Winforms apps.
''Dim instanceName As String = System.Reflection.Assembly.GetEntryAssembly.GetName.Name
'''Dim instanceName As String = System.Web.Hosting.HostingEnvironment.ApplicationID
' Must replace special characters like (, ), #, /, \\
Dim instanceName2 As String = AppDomain.CurrentDomain.FriendlyName.ToString.Replace("(", "[").Replace(")", "]").Replace("#", "_").Replace("/", "_").Replace("\\", "_")
'For ASP.NET applications your instanceName will be your CurrentDomain's
'FriendlyName. Replace the line above that sets the instanceName with this:
Dim instanceName As String = AppDomain.CurrentDomain.FriendlyName.ToString.Replace("(", "[") _
.Replace(")", "]").Replace("#", "_").Replace("/", "_").Replace("\\", "_")
Dim pid As String = GetCurrentProcessId.ToString
instanceName = (instanceName + ("[" & (pid & "]")))
Console.WriteLine("Instance Name: {0}", instanceName)
Console.WriteLine("---------------------------")
Return instanceName
End Function
End Class
もちろんapp.configの代わりにweb.configに以下の追加を行います。
web.config
<system.codedom>
・・・・
</system.codedom>
<system.diagnostics>
<switches>
<add name="ConnectionPoolPerformanceCounterDetail"
value="4"/>
</switches>
</system.diagnostics>
</configuration>
このようなクライアントからのアクセスと、SQLSERVERでのコネクション状況を確認すると見えてくるのが、
コネクションはクライアントが管理しているということです。
SQLSERVER側で
select hostname,count(*) from master..sysprocesses where hostname!='' group by hostname
というコマンドで接続状況を監視してみてください。
WEBサーバー側のweb.configを変更しなくて結構ですので上書きしてみてください。
もし、Closeされていなくて待ちとなっていたコネクションがあればCloseされることが確認できるはずです。
SQLSERVER側ではClose処理をしたコネクションなのか、まだ使用しているコネクションなのかが明確にはわかりません。
それはSQLSERVER側からみたら、どちらも使用中のコネクションだからです。
コネクションプールという概念は、実はクライアント側からみたコネクションの概念なのです。
再利用するためにプールに保存してるか、まだ使用中なのかはクライアントからみた概念なのです。
何にせよCloseは重要です。
Closeせずに再度接続をすればコネクションは増えます。
もしCloseしたコネクションがあれば再利用します。
Webアプリケーションでは、exeのように明確なアプリケーションの終了というタイミングがありません。
Close処理は、文字通りのClose処理ではなく、再利用可能であるという信号をおくるだけになります。
この点を注意しなくては、IISによる大規模なシステムは構築できません。
補足事項としてもう一点、コネクションはDataReaderにも発します。
コネクションから取得したデータをDataReaderに持たせるとします。
すると2つのコネクションが発生します。
リソースは閉じるという基本が重要です。
リソースはusingを使用すべし!ともいえるかもしれません。
