【実務・中級編】上級プロフェッショナル向け:VB.NETでの「AssemblyName」と「Assembly.LoadFrom」によるアドイン機構:プラグイン型アーキテクチャの構築 – Visual Basic (VB / VB.NET)解析バイブル

スポンサーリンク

枯れた言語VB.NETで実現する「拡張性」:動的アセンブリロードによるプラグイン設計の極意

多くの開発者がVB.NETを「レガシー」と呼ぶ。だが、アーキテクトの視点から言えば、それは誤解だ。VB.NETは.NETの完全なパワーを享受できる言語であり、その堅牢な型システムとLINQの強力な抽象化能力は、ビジネスロジックを分離する「プラグイン型アーキテクチャ」において、極めて高い保守性を発揮する。

今日は、メインアプリケーションを一行も書き換えることなく、DLLを配置するだけで機能が拡張される「プロフェッショナルなアドインシステム」の構築法を伝授する。

1. なぜ「静的参照」ではいけないのか

業務効率化ツールを開発する際、多くの初心者は参照設定(Reference)で直接DLLを紐付ける。しかし、これでは機能追加のたびに再コンパイルが必要となり、メインアプリのバイナリが肥大化し、依存関係の地獄(DLL Hell)に陥る。

真のプロフェッショナルは、「インターフェース」を定義した共通ライブラリのみを静的に持ち、実装は実行時に動的ロードするという疎結合(Loose Coupling)を選択する。

2. アーキテクチャの要:インターフェースの分離

まず、メインアプリとプラグインが共通して参照する「契約(Contract)」ライブラリを作成する。ここには機能の定義のみを記述する。

‘ IPlugin.vb (共通ライブラリ)
Public Interface IPlugin
ReadOnly Property Name As String
Sub Execute()
End Interface

3. 「Assembly.LoadFrom」による動的ロードの実装

ここが本題だ。`Assembly.LoadFrom` を使えば、実行時に外部ファイルをメモリにマッピングし、インスタンス化できる。ただし、ここで注意が必要なのは「例外ハンドリング」と「型安全性の担保」である。

Imports System.Reflection
Imports System.IO

Public Class PluginLoader
”’

”’ 指定ディレクトリからプラグインをロードし、実行する
”’

Public Sub LoadAndRunPlugins(folderPath As String)
If Not Directory.Exists(folderPath) Then Exit Sub

For Each dllPath As String In Directory.GetFiles(folderPath, “.dll”)
Try
‘ アセンブリをロード
Dim asm As Assembly = Assembly.LoadFrom(dllPath)

‘ インターフェースを実装している型を探す
For Each t As Type In asm.GetExportedTypes()
If t.GetInterface(GetType(IPlugin).FullName) IsNot Nothing Then
‘ インスタンス生成
Dim plugin As IPlugin = DirectCast(Activator.CreateInstance(t), IPlugin)

‘ 実行
Console.WriteLine($”実行中: {plugin.Name}”)
plugin.Execute()
End If
Next
Catch ex As Exception
‘ 本番環境ではここでログを吐き、個別のプラグインエラーが全体を停止させないようにする
Debug.WriteLine($”プラグインロード失敗: {dllPath} – {ex.Message}”)
End Try
Next
End Sub
End Class

4. 堅牢な設計のために:プロフェッショナルが守る3つの鉄則

動的ロードを扱う際、以下の3点に妥協してはならない。

① アセンブリのアンロードを考慮する

`Assembly.LoadFrom` でロードしたDLLは、デフォルトのロードコンテキストに居座り続ける。頻繁に読み書きが発生する場合は、`AssemblyLoadContext`(.NET Core/.NET 5+以降)を使用し、不要になったメモリを解放する設計が必須だ。

② ファイルロックの回避

DLLが使用中であると更新ができない。開発中、頻繁にDLLを差し替える必要があるなら、`Assembly.Load(File.ReadAllBytes(path))` を使い、一度メモリにロードしてから反射させる方法を検討せよ。これによりファイルハンドルが解放される。

③ データベース連携時のトランザクション境界

プラグイン内でDB操作を行う場合、メインアプリ側の接続を渡すのか、プラグイン側で完結させるのかを明確に定義せよ。「プラグインごとにDBコネクション文字列を読み込ませる」設計にしておけば、メインアプリに依存しない独立したテストが可能になる。

最後に:コードは「資産」ではなく「負債」になり得る

私がVB.NETを好む理由は、その冗長な構文が「何をしているか」を明示的にし、後任者が修正を加える際の認知負荷を下げてくれるからだ。

今回紹介したプラグイン機構を導入すれば、現場のエンジニアは「メインアプリの開発者に頼まずに、自分たちで機能追加できる」ようになる。これこそが、業務自動化ツールにおける「スケールする設計」である。

迷わず実装せよ。ただし、インターフェースの契約だけは、魂を込めて設計することだ。

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