この記事は Visual Basic Advent Calendar 2018 の 23日目の記事です。
DB のバージョンに左右される現場の例
エンプラ系で VB.NET + Oracle を採用している場合、妙な環境で開発されている方も多いのではないかと思います。
例えば、
- 開発環境と本番環境で、Oracleのバージョンが違う(bit数も違ってたり)
- 開発者全員が、同一の環境を用意していない
など。
本番直前のタイミングで
「あ、開発は11でやってもらったけど、本番環境はバージョン12で動かすからねー。」とか言われて、正気を保てなくなったエンジニアも何人か居るんじゃないかと思います。
(開発環境は過去のものを使いまわして、本番環境はアップデートしているというパターン。当然、メンバーには周知されていない。)
そこまでダメな事例を出さずとも、バージョンが上がるたびに ODBCのバージョンを変えたり、
参照設定を変えたりするのが面倒くさいと感じる方も多いのではないかと思います。
ODBC固有の機能を使用しているわけでもなく、ORマッパーも使用しておらず、ゴリゴリと生SQLを書いているだけなら、別に参照設定して Oracle専用のドライバを指定せずとも、DBへの接続が可能です。
具体的には、DbProviderFactories という、抽象化されたクラスを使用します。
コーディング例:プロバイダー側
Imports System.Data.Common
Imports System.Data
Public Class DataAccess
Implements IDisposable
Private conn As DbConnection = Nothing
Private factory As DbProviderFactory = Nothing
Private tran As DbTransaction = Nothing
Private _hasAccessError As Boolean = False
Private _hasSQLError As Boolean = False
Private _errorMessage As String = String.Empty
Private _sqlContents As String = String.Empty
Private isDisposed As Boolean = False
Public Sub New()
Dim dcsb As DbConnectionStringBuilder
Try
_hasAccessError = False
factory = DbProviderFactories.GetFactory("Oracle.DataAccess.Client")
dcsb = factory.CreateConnectionStringBuilder
dcsb("Data Source") = ConfigurationManager.AppSettings("dataSource")
dcsb("User ID") = ConfigurationManager.AppSettings("userId")
dcsb("Password") = ConfigurationManager.AppSettings("pass")
conn = factory.CreateConnection()
conn.ConnectionString = dcsb.ConnectionString
conn.Open()
tran = conn.BeginTransaction()
_hasSQLError = False
_errorMessage = String.Empty
Catch ex As Exception
_hasAccessError = True
_errorMessage = ex.Message
End Try
End Sub
Public Function ExcuteQuery(ByVal SQLContents As String) As DataSet
Dim cmd As DbCommand
Dim da As DbDataAdapter
Dim ds As DataSet = New DataSet
Try
_hasSQLError = False
_sqlContents = SQLContents
cmd = conn.CreateCommand
cmd.CommandText = SQLContents
da = factory.CreateDataAdapter()
da.SelectCommand = cmd
da.Fill(ds)
Catch ex As Exception
_hasSQLError = True
_errorMessage = ex.Message
End Try
Return ds
End Function
Public Function ExecuteNonQuery(ByVal SQLContents As String, Optional ByVal isCommit As Boolean = True) As Integer
Dim cmd As DbCommand
Dim affectedRows As Integer = 0
Try
_hasSQLError = False
_sqlContents = SQLContents
cmd = conn.CreateCommand
cmd.CommandType = CommandType.Text
cmd.CommandText = SQLContents
affectedRows = cmd.ExecuteNonQuery()
If isCommit Then
Commit()
End If
Catch ex As Exception
_hasSQLError = True
_errorMessage = ex.Message
Rollback()
End Try
Return affectedRows
End Function
Public Sub Commit()
Try
tran.Commit()
tran = conn.BeginTransaction()
Catch ex As Exception
End Try
End Sub
Public Sub Rollback()
Try
tran.Rollback()
tran = conn.BeginTransaction()
Catch ex As Exception
End Try
End Sub
End Class
Dispose や Finalize 、他色々と省略しています。
コーディング例:プロバイダー側の設定
web.config に設定内容を記述します。
<configuration>
<appSettings>
<add key="dataSource" value="YOUR_SCHEMA" />
<add key="userId" value="SYSTEM" />
<add key="pass" value="SYSTEM" />
</appSettings>
</configuration>
コーディング例:使用側
Using dbAccess As New DataAccess
ds = dbAccess.ExcuteQuery("SELECT * FROM users")
affectedRows = dbAccess.ExecuteNonQuery("UPDATE projects SET status=1")
End Using
端折りすぎてますが、雰囲気だけ伝わればいいかなぐらいで書いてます。
ちゃんと書くと長いんで。
解説
プロバイダー側の以下のコードにて、DB接続のオブジェクトを生成しています。
factory = DbProviderFactories.GetFactory("Oracle.DataAccess.Client")
端末に OracleClientがインストールされていれば、勝手にデフォルトで使用するバージョンを拾って、
後はよしなにやってくれます。
(10g がインストールされている端末では 10gの Clientを見つけ出し、11g がインストールされている端末では、11g の Clientを探し出す。)
なので、Oracle Client さえインストールされていれば、コネクション設定をこねくり回す必要もなくなっています。
Oracle Server をインストールすると、Oracle Clientも自動でインストールされるので、どちらで動かす場合も問題ないかと思われます。
また、引数に "Oracle.DataAccess.Client" を渡して Oracle接続用のオブジェクトを生成していますが、
恐らく、SQLServer や MySQL でも同じ方法で行けるんじゃないかと思います。
余談ですが、上記の「Factory」は、多分、デザインパターンにおける Facory Method の事。