【実務・中級編】【上級プロフェッショナル向け】SQL Server上の図面管理テーブルと同期する「チェックイン・チェックアウト」オープン制御 – AutoCAD VBA解析バイブル

スポンサーリンク
スポンサーリンク

図面管理の「真実」をSQL Serverに刻め:堅牢なチェックイン・チェックアウト制御の設計思想

多くの開発者が、図面管理システムを構築しようとする際に陥る致命的な罠がある。それは、「ファイルシステム(OS)の属性に、管理の主導権を委ねてしまうこと」だ。

「ファイルが書き込み禁止なら読み取り専用にする」「特定の名前のロックファイルを作っておく」——。もし君がそんな設計を考えているなら、今すぐその思考を捨ててほしい。それはシステムではなく、単なる「運任せの仕組み」だ。ネットワークの瞬断、AutoCADの予期せぬクラッシュ、あるいはマルチユーザー環境でのレースコンディション(競合状態)。それらが起きた瞬間、君の作った仕組みは崩壊し、図面の整合性は失われる。

プロフェッショナルが目指すべきは、データベース(SQL Server)を「唯一の真実(Single Source of Truth)」とする、アトミック(原子性)な制御だ。

本稿では、AutoCAD VBAを用いながら、SQL Serverと完全に同期した「チェックイン・チェックアウト」制御を実装するための、極限の設計論と実装手法を伝授する。

1. 失敗する設計 vs 勝利する設計

なぜ、ファイル属性による制御では不十分なのか? その理由は「状態の非連続性」にある。

❌ アンチパターン:ファイル属性・ロックファイル方式

  • 問題点1:不完全な解放:ユーザーがAutoCADを強制終了したり、PCがフリーズしたりすると、ロックファイルが残ったままになる。これを手動で消す運用は、スケールしない。
  • 問題点2:競合の発生:二人が同時にファイルを開こうとした際、OSレベルのファイルロック判定と、アプリケーションの論理判定の間に隙間が生じる。
  • 問題点3:監査ログの欠如: 「誰が、いつ、何の目的で」開いたのかという履歴が、ファイルシステムからは追えない。

✅ プロフェッショナル・パターン:DB中央管理方式

  • 解決策1:アトミックな更新:SQLのトランザクションを利用し、「ステータスの更新」と「ユーザー情報の記録」を一つの不可分な操作として実行する。
  • 解決策2:セッション管理:DB側に「セッションID」や「最終生存確認(Heartbeat)」を持たせることで、異常終了したロックを自動検知・解放できる。
  • 解決策3:完全なトレーサビリティ:すべての操作がSQL Serverに刻まれるため、監査ログが自動的に生成される。

2. データベース設計(Schema Design)

まずは、SQL Server側に用意すべき最小限かつ最強のテーブル構造を定義する。

CREATE TABLE DWG_Management (
    DWG_ID INT IDENTITY(1,1) PRIMARY KEY,
    File_Path NVARCHAR(500) NOT NULL UNIQUE, -- 図面ファイルのフルパス
    Status INT NOT NULL DEFAULT 0,           -- 0: Available, 1: Checked-out
    Locked_By NVARCHAR(100),                -- ロックしているユーザー名
    Machine_Name NVARCHAR(100),             -- ロックしているPC名
    Last_Update_DT DATETIME DEFAULT GETDATE(), -- 最終更新日時
    Version_No INT DEFAULT 1                 -- バージョン管理用
);

-- 監査ログ用テーブル
CREATE TABLE DWG_Audit_Log (
    Log_ID INT IDENTITY(1,1) PRIMARY KEY,
    DWG_ID INT,
    Action_Type NVARCHAR(50),               -- 'CHECKOUT', 'CHECKIN', 'ERROR'
    User_Name NVARCHAR(100),
    Action_DT DATETIME DEFAULT GETDATE(),
    Details NVARCHAR(MAX)
);

3. 実装:堅牢なマネージャークラスの構築

VBAでこれを実装する場合、`ThisDrawing` モジュールにロジックを詰め込んではいけない。それは保守性を著しく低下させる。制御ロジックは、独立したクラスモジュールにカプセル化すべきだ。

以下に、プロダクション環境で使用に耐えうる、ADODBを用いた実装例を示す。

クラスモジュール: `clsDWGManager`

' ==============================================================================
' Class: clsDWGManager
' Purpose: SQL Serverと連携した図面ロック制御のコアロジック
' ==============================================================================
Option Explicit

Private Const CONN_STR As String = "Provider=SQLOLEDB;Data Source=YOUR_SERVER;Initial Catalog=YOUR_DB;Integrated Security=SSPI;"
Private m_FilePath As String
Private m_UserName As String

' 初期化
Private Sub Class_Initialize()
    m_UserName = Environ("USERNAME")
End Sub

' 図面のチェックアウト(ロック取得)を試みる
' Returns: True if successful, False if already locked
Public Function CheckOut() As Boolean
    Dim conn As Object ' ADODB.Connection
    Dim cmd As Object  ' ADODB.Command
    Dim sql As String
    
    Set conn = CreateObject("ADODB.Connection")
    Set cmd = CreateObject("ADODB.Command")
    
    On Error GoTo ErrorHandler
    conn.Open CONN_STR
    
    ' トランザクションの開始:アトミックな更新を保証する
    conn.BeginTrans
    
    ' 1. 現在のステータスを確認し、かつ自分以外がロックしていないことを条件に更新
    ' ここで「行ロック」をかけるのがプロの技だ
    sql = "UPDATE DWG_Management SET " & _
          "Status = 1, " & _
          "Locked_By = '" & m_UserName & "', " & _
          "Machine_Name = '" & Environ("COMPUTERNAME") & "', " & _
          "Last_Update_DT = GETDATE() " & _
          "WHERE File_Path = '" & m_FilePath & "' " & _
          "AND (Status = 0 OR (Status = 1 AND Locked_By = '" & m_UserName & "'))"
    
    Set cmd.ActiveConnection = conn
    cmd.CommandText = sql
    cmd.Execute
    
    ' 2. 更新された行数を確認(0なら他者がロック中)
    If cmd.RecordsAffected > 0 Then
        conn.CommitTrans
        CheckOut = True
    Else
        conn.RollbackTrans
        CheckOut = False
    End If
    
    conn.Close
    Exit Function

ErrorHandler:
    If Not conn Is Nothing Then
        If conn.State = 1 Then conn.RollbackTrans
    End If
    MsgBox "DB Error: " & Err.Description, vbCritical, "System Error"
    CheckOut = False
End Function

' 図面のチェックイン(ロック解除)
Public Sub CheckIn()
    Dim conn As Object
    Dim sql As String
    
    Set conn = CreateObject("ADODB.Connection")
    On Error GoTo ErrorHandler
    
    conn.Open CONN_STR
    
    sql = "UPDATE DWG_Management SET " & _
          "Status = 0, " & _
          "Locked_By = NULL, " & _
          "Machine_Name = NULL, " & _
          "Last_Update_DT = GETDATE() " & _
          "WHERE File_Path = '" & m_FilePath & "' " & _
          "AND Locked_By = '" & m_UserName & "'"
    
    conn.Execute sql
    conn.Close
    Exit Sub

ErrorHandler:
    MsgBox "CheckIn Failed: " & Err.Description, vbExclamation
End Sub

' パスのセット
Public Property Let FilePath(ByVal vNewValue As String)
    m_FilePath = vNewValue
End Property

AutoCAD イベントハンドラ: `ThisDrawing`

次に、AutoCADのイベント(ファイルオープン・クローズ)と、上記のマネージャークラスを結合する。

' ==============================================================================
' Module: ThisDrawing
' Purpose: AutoCAD図面イベントのインターセプト
' ==============================================================================
Option Explicit

Private m_Manager As clsDWGManager
Private m_IsReadOnlyMode As Boolean

Private Sub Document_Open()
    Dim fullPath As String
    fullPath = ThisDrawing.FullName
    
    ' ファイルが保存されていない新規図面はスキップ
    If fullPath = "" Then Exit Sub
    
    Set m_Manager = New clsDWGManager
    m_Manager.FilePath = fullPath
    
    ' チェックアウト試行
    If m_Manager.CheckOut() Then
        m_IsReadOnlyMode = False
        ' 成功:編集可能
    Else
        ' 失敗:他者が使用中
        m_IsReadOnlyMode = True
        MsgBox "【警告】この図面は他のユーザーによって編集されています。" & vbCrLf & _
               "読み取り専用モードで開きます。", vbInformation, "図面管理システム"
        
        ' 実務上のテクニック:ここで図面を強制的に読み取り専用として扱うロジックを走らせる
        ' (AutoCAD自体をReadOnlyで開くのは難しいため、保存処理をブロックする設計にする)
    End If
End Sub

Private Sub Document_Close()
    ' 編集モードだった場合のみチェックインを実行
    If Not m_IsReadOnlyMode And Not m_Manager Is Nothing Then
        m_Manager.CheckIn
    End If
    Set m_Manager = Nothing
End Sub

' 保存時に読み取り専用モードでないかチェックする(重要!)
Private Sub Document_Save()
    If m_IsReadOnlyMode Then
        MsgBox "読み取り専用モードのため、保存できません。", vbCritical, "エラー"
        ' 保存処理を中断させるためのトリガー(必要に応じて実装)
    End If
End Sub

4. 伝説のアーキテクトが教える「運用上の急所」

コードを書いただけで満足してはいけない。このシステムを「現場で使える」レベルに引き上げるには、以下の3点を考慮せよ。

① 「ゾンビロック」への対策(Heartbeat方式)

AutoCADがクラッシュした場合、`Document_Close` は呼ばれない。その図面は永遠に「使用中」としてDBに残ってしまう。これを防ぐために、DB側に `Last_Update_DT` を持たせている。
運用ルール: 「最終更新から30分以上経過しているロックは、強制解放して良い」というロジックを、管理者用ツールまたはSQLのバッチジョブとして実装しておけ。

② レースコンディションの完全回避

上記のコードでは `UPDATE … WHERE Status = 0` という条件を用いている。これは、「読み取り(SELECT)と書き込み(UPDATE)を分離しない」という鉄則に基づいている。
「現在の状態を確認してから、UPDATEする」という2ステップを踏むと、その僅かな間に他者が割り込む隙ができる。SQLの単一文で条件判定と更新を完結させること。これがアトミック性の確保だ。

③ ネットワーク遅延への耐性

DBへの接続は、必ず `On Error` でハンドリングせよ。ネットワークが切断された瞬間に、図面が開けなくなるのはユーザーのストレスになる。
「DB接続失敗時は、ローカルのファイル属性をチェックするフォールバックモードに移行する」といった、Graceful Degradation(優雅な機能縮退)の設計を検討しろ。

結論

図面管理システムにおける「チェックイン・チェックアウト」は、単なるファイルのロックではない。それは、データの整合性を守るための「契約」である。

DBを真実の源とし、トランザクションを武器に、例外的な事態をあらかじめ設計に組み込む。この執念こそが、プロフェッショナルな自動化エンジニアと、単なるVBA使いを分かつ境界線である。

幸運を。

タイトルとURLをコピーしました