たにぐちBLOG

C#が好きでたまんない

  PASSJブログ :: ホーム :: 連絡をする :: RSS  :: ATOM :: Login
  31 投稿数 :: 0 ストーリー :: 57 コメント :: 10 トラックバック

過去の記事

カテゴリ

イメージギャラリ

http://blogs.sqlpassj.org/masatotaniguchi/archive/2005/06/10/9475.aspx
のASP版(非ASP.NET)です。ActiveX dllを使用し、ASPファイルからデータアクセス部分を分離、少ない変更で複数のデータベースに対応できるようにします。

Visual Basic 6.0で、ActiveX dllを作成し、ScottDAOという名前をつけます。これは、以前にご紹介したVB6版をActiveX dll化したものです。
※以下のソースコードはエラー処理などが省略してあり、完全ではありません。

テーブル(OracleのScott.Empテーブルと、それをSQL Server 2000にインポートしたテーブル)のレコードを保存するために、VO(Value Object)を用意します。これはVB6版と全く同じでそのまま移植できます。
Instancingプロパティは5-MultiUseとします


' ソースコード(clsEmpVo):クラスモジュール

' Private 変数
' 従業員番号
Private m_lngEmpNo As Long
' 従業員名
Private m_strEname As String

' 以下略

' プロパティ

Public Property Get EMPNO() As Long
    EMPNO = m_lngEmpNo
End Property

Public Property Let EMPNO(lngEmpNo As Long)
    m_lngEmpNo = lngEmpNo
End Property

Public Property Get Ename() As String
    Ename = m_strEname
End Property

Public Property Let Ename(strEname As String)
    m_strEname = strEname
End Property

' 以下略(フィールド分同じものを作ります)


インターフェースクラスは外部に公開したいため、Instancingプロパティを5-MultiUseとします。
これをこのまま公開しても、中は空っぽです。後でちょっと細工をします。




' ソースコード(clsEmpDao) :クラスモジュール

Public Function SelectEmp(lngDeptNo As Long) As Collection

End Function

Public Function InsertEmp(EmpVo As clsEmpVo) As Boolean

End Function

Public Function UpdateEmp(EmpVo As clsEmpVo) As Boolean

End Function

Public Property Set Connection(con As Object)
   
End Property


clsEmpSQLServerDaoはVB6版とほとんど同じです(選択条件が部署番号になっている部分だけが違います)。このクラスは外部に公開しないため、Instancingプロパティを1-Privateとします。
Oracleについても全く同じなので、ここでは省略します。VB6版をご参照ください。
なお、nvl関数は標準モジュールにある独自の関数で動作はORACLEのNVLと全く同じです。


' ソースコード(clsEmpSQLServerDao):クラスモジュール

Implements clsEmpDao

Private Connection As Object

Public Property Set clsEmpDao_Connection(con As Object)
    Set Connection = con
End Property

Public Function clsEmpDao_SelectEmp(lngDeptNo As Long) As Collection

    Dim cmd As New ADODB.Command
    Dim result As New Collection
    Dim rs As ADODB.Recordset
   
    ' Commandの設定
    With cmd
        .ActiveConnection = Connection
        .CommandText = "SELECT * FROM EMP WHERE DEPTNO = ?"
        .CreateParameter , adInteger, adParamInput
        .Parameters(0).Value = lngDeptNo
    End With
   
    ' SQLを実行して結果セットを取得します
    Set rs = cmd.Execute

    ' Recordsetから、VOへ値を詰めます
    Do Until rs.EOF
        Dim EmpVo As clsEmpVo
        Set EmpVo = New clsEmpVo
        EmpVo.EmpNo = nvl(rs.Fields("EMPNO"), 0)
        EmpVo.Ename = nvl(rs.Fields("ENAME"), "")
        EmpVo.Job = nvl(rs.Fields("JOB"), "")
        EmpVo.Mgr = nvl(rs.Fields("MGR"), 0)
        EmpVo.HireDate = nvl(rs.Fields("HireDate"), 0)
        EmpVo.Sal = nvl(rs.Fields("SAL"), 0)
        EmpVo.Comm = nvl(rs.Fields("COMM"), 0)
        EmpVo.DeptNo = nvl(rs.Fields("DEPTNO"), 0)
        Call result.Add(EmpVo)
        rs.MoveNext
    Loop
   
    ' 後始末
    rs.Close
    Set rs = Nothing
    ' 戻り値(VOのコレクション)の設定
    Set clsEmpDao_SelectEmp = result
End Function

Public Function clsEmpDao_InsertEmp(EmpVo As clsEmpVo) As Boolean
    ' 省略
End Function

Public Function clsEmpDao_UpdateEmp(EmpVo As clsEmpVo) As Boolean
    ' 省略
End Function


さて、公開したいのはEmpDaoですがASPに渡したいのは中身の詰まったクラスです。
そこで以下のようなクラスを作成し外部に公開します(Instancingプロパティを5-MultiUse)。
データベースを変更する必要が出た時はここを変更します。


' ソースコード(clsScottDaoFactory):クラスモジュール

' このクラスが生成するDAOは、clsScottDaoFactory
' のインスタンスが存命中の場合のみ使用できます。
' clsScottDaoFactoryインスタンスの消滅とともにDB
'
接続も自動的にCloseされるためです。

Private Connection As clsConnection

Public Function EmpDAO() As clsEmpDao
   Dim emp As clsEmpDao
   ' DB変更時、コードの変更が必要な場所
   Set emp = New clsEmpSQLServerDao
   ' Set emp = New clsEmpOracleDao
   Set Emp.Connection = Connection.GetConnection   
   Set EmpDAO = emp
End Function

Private Sub Class_Initialize()
   Dim ConnectionFactory As New clsConnectionFactory
   Set Connection = ConnectionFactory.GetClsConnection
End Sub

' このメソッドは使用後、なるべく早く実行するようにします。
Public Sub CloseConnection()
    Connection.CloseConnection
    Set Connection = Nothing
End Sub

' 安全策
Private Sub Class_Terminate()
    If Not Connection Is Nothing Then
        Connection.CloseConnection
    End If
End Sub


DBの接続のためのクラス、clsConnectionを定義します。これはインターフェースとして扱います(Instancingプロパティを5-MultiUse)。


' ソースコード(clsConnection):クラスモジュール

Public Function GetConnection() As Object
   
End Function

Public Sub CloseConnection()
   
End Sub


SQLServerのためのclsConnectionの実装クラスを定義します(Instancingプロパティを1-Private)。Oracle用のclsOracleConnectionは省略します。


' ソースコード(clsSQLServerConnection):クラスモジュール

Implements clsConnection

Private adoSQLServerCon As ADODB.Connection

Public Function clsConnection_GetConnection() As Object
    Set clsConnection_GetConnection = adoSQLServerCon
End Function

Public Sub clsConnection_CloseConnection()
    adoSQLServerCon.Close
    Set adoSQLServerCon = Nothing
End Sub

Private Sub Class_Initialize()
   Set adoSQLServerCon = New ADODB.Connection
    adoSQLServerCon.ConnectionString = _
        "PROVIDER=SQLOLEDB;SERVER=ServerName;DATABASE=Scott;UID=sa;PWD=*******"
    adoSQLServerCon.Open
End Sub


clsConnectionクラスを作成するためのクラスを定義します(Instancingプロパティを5-MultiUse)。

' ソースコード(clsConnectionFactory):クラスモジュール

Public Function GetClsConnection() As clsConnection
   ' DB変更時、コードの変更が必要な場所
  
Set GetClsConnection = New clsSQLServerConnection
   ' Set GetClsConnection = New clsOracleConnection
End Function


以上のActiveX dllを作成しレジストリに登録します。これをテストするためのASPファイルを用意しました。
For Eachが不安な方は、レコード数をresult.countで取得できるのでFor...Next文で書き換えてもいいと思います。


' ソースコード(ScottEmp.asp):aspファイル

<HTML>
<BODY>
<%
Set scottDAOFactory = Server.CreateObject("ScottDAO.clsScottDAOFactory")
Set empDAO = scottDAOFactory.EmpDAO
Set result = empDAO.SelectEmp(20)
scottDAOFactory.CloseConnection
%>
<TABLE border="1">
<TR>
   <TH>従業員番号</TH>
   <TH>名前</TH>
   <TH>職種</TH>
   <TH>上司</TH>
   <TH>入社日</TH>
   <TH>給料</TH>
   <TH>歩合</TH>
   <TH>部署番号</TH>
</TR>
<%
For Each empVo In result
%>
<TR>
   <TD><%=empVo.EmpNo%></TD>
   <TD><%=empVo.Ename%></TD>
   <TD><%=empVo.Job%></TD>
   <TD><%=EmpVo.Mgr%></TD>
   <TD><%=EmpVo.Hiredate%></TD>
   <TD><%=empVo.Sal%></TD>
   <TD><%=EmpVo.Comm%></TD>
   <TD><%=empVo.deptNo%></TD>
</TR>
<%
Next
%>
</TABLE>
</BODY>
</HTML>


基本方針は、

aspファイルには画面表示ロジック以外はなるべく書かないようにする

ということです。ビジネスロジックもActiveX dllでカプセル化し、aspファイルからはそれを呼ぶだけにするようにします。

投稿日時 : 2005年7月9日 2:43

コメントを追加

# re: ASP(非ASP.NET)で、なるべくコードの変更を少なくして複数のデータベースに確実に対応できるようにする 2005/07/14 10:48 大西 彰
おっしゃる通り、aspファイルを簡略化し、ビジネスロジックをActiveX Dllでカプセル化するLayerアプローチが大切だと思います。これを知らない人は、ベタでaspファイルの中にロジックも画面表示もぐちゃまぜにするために、保守が困難なWebアプリケーションが簡単にできてしまいます。

余談ですが、ActiveX Dllでビジネスロジックをカプセル化する場合、クエリーの結果が大量に存在する場合は要注意です。サーバのリソースが少ない場合、見かけ上、フリーズすることがあるためです。実際はフリーズしているのではなく、仮想メモリをなんとかやりくりしようとページングが発生しているのですが、SQL Server側がどんどんメモリを消費してしまうので、追いつかなくなって、レスポンスがなくなるという現象が起こることがあります。
DAOのアプローチにおいても、安全策として、クエリーの件数を一回取得するということを考慮した方がいい場合があります。一定件数を越えている場合は、検索をやり直させるというものです。

ご参考までに。

# re: ASP(非ASP.NET)で、なるべくコードの変更を少なくして複数のデータベースに確実に対応できるようにする 2005/07/14 20:28 たにぐち
コメントありがとうございます。JSPでもカスタムタグなどを使い、スクリプトレットをなるべく減らしたりデータアクセス、ビジネスロジックを分離したりするのは一般的になっていますね。やはりそこでも件数が多い場合ご指摘の通りの問題が発生します。最大件数のみを表示するとか場合によって色々方法がありますが。。Javaではすでにこのような手法は確立した観がありますが、ASPやVB6では、それが確立されなかったのはある意味悲劇だったと思います。実力を十分発揮できないまま世代交代してしまいました。VB6は簡単でこれを使えばどんな初心者でもすぐシステム開発ができるという錯覚のために、現場が混乱し信用低下を招いたのがVB衰退の一因ではないでしょうか。

# re: ASP(非ASP.NET)で、なるべくコードの変更を少なくして複数のデータベースに確実に対応できるようにする 2005/07/15 17:29 大西 彰
おっしゃるとおり、VB6+Active Server Pagesでも十分すごいことができるのですのにね。もったいない。せっかくいい道具があっても使いこなす側が下手だととんでもないプログラムが出来上がってしまいますよね。Layeredパターンはそんなに難しくなく、保守の面でも最適なアプローチです。下の層にすべてお任せすればいいわけですから、ある種の委譲(Delegation)なんですけどね。
ADOがADO.NETになって、プログラミングアプローチは異なりました。オフラインレコードセットを取得することが簡単になった点は評価できますが、ASP.NETになってもLayeredパターンによる開発を行わないと悲劇が起こります。いくら
コードビハインドが可能になったからといって、ベタベタのデータアクセスをしていれば、ASPでスクリプトだけで開発しているのと何等変わりません。NECで教育を担当されている山崎さんもn-tierでの開発を啓蒙活動しています。昨年、TechEdでちょこっとお話ししましたが、データアクセスとビジネスロジックの分離、それとWebページレンダリングの分離は重要課題です。VS2005になって、また最新テクノロジーが登場しますが、基本原理は変わらないでしょう。
たにぐちさんのように、デザインパターンをきちんと押さえてプログラミングされている人が増えることを期待しています。そういった意味で、たにぐちさんの今後の投稿には期待しています。

# re: ASP(非ASP.NET)で、なるべくコードの変更を少なくして複数のデータベースに確実に対応できるようにする 2005/07/16 18:23 たにぐち
ありがとうございます。私はASP.NETなどの最新のテクノロジが、VB6やASPと同じ轍を踏んでほしくないと思っています。それには、やはり大西さんのような方や経験のある教育者の方の助けが必要だと思います。

# Visual Basic 6.0で、なるべくコードの変更を少なくして複数のデータベースに確実に対応できるようにする 2005/07/20 20:21 たにぐちBLOG
Visual Basic 6.0で、なるべくコードの変更を少なくして複数のデータベースに確実に対応できるようにする

# ASPのActiveX dllを使った連動プルダウンの実装 2005/07/21 2:37 たにぐちBLOG
ASPのActiveX dllを使った連動プルダウンの実装

コメント

タイトル:
名前:
Url:
コメント: