【入門編】VB.NETでの例外情報取得(Exception.StackTraceとInnerException):マルチレイヤーアプリにおけるエラー追跡の強化 – Visual Basic (VB / VB.NET)解析バイブル

スポンサーリンク

こんにちは!開発現場を渡り歩くうちに、いつの間にか「コードの医者」なんて呼ばれるようになったシニアエンジニアです。

皆さんは、VB.NETを使った業務システムの開発で、こんな恐怖を味わったことはありませんか?

「ユーザーから『エラーが出ました』と言われたけれど、ログを見ても『System.Exception: 予期せぬエラーが発生しました』としか書かれていない……。本当の犯人はどこにいるんだ!?」

画面の向こうで冷や汗をかいた経験、きっと一度や二度ではないはずです。
特に、画面(UI)層、業務ロジック層、データアクセス層と綺麗にレイヤーを分けた「多層アーキテクチャ(マルチレイヤー)」のシステムでは、エラーが起きた場所からログが出る場所までに距離があります。

ここを綺麗にクリアできるようになると、VB.NETの構造がスッと頭に入り、ワンランク上のエンジニアへとグッと近づけますよ。
今回は、エラーの根本原因を根掘り葉掘り暴き出す「InnerExceptionとStackTraceの極意」を、一緒に紐解いていきましょう!

1. なぜマルチレイヤーアプリでエラー追跡が迷宮入りするのか?

まずは、よくある「やってはいけないエラー処理」の現場を覗いてみましょう。

.net
‘ 【アンチパターン】すべての例外を雑に握りつぶして丸めるコード
Public Sub SaveUserData(userData As User)
Try
‘ データアクセス層を呼び出し(ここでDB接続エラーが発生したとする)
_dataAccess.ExecuteInsert(userData)

Catch ex As Exception
‘ ❌ やっちゃったパターン:元のエラーを無視して新しい例外を投げ直す
Throw New Exception(“ユーザーの保存に失敗しました。”)
End Sub
End Sub

このコード、何が問題か分かりますか?
呼び出し元(UI層)には「ユーザーの保存に失敗しました。」というメッセージしか届きません。
元の例外(「SQL Serverに接続できませんでした」や「主キーが重複しています」といった本当の原因(Root Cause))が、新しく作った `Exception` に上書きされて、闇の中に消えてしまったのです。これではデバッグのしようがありませんね。

2. 救世主「InnerException」:エラーのバトンリレーをつなぐ

プロの世界では、下層で起きた例外を握りつぶすのではなく、「包み込んで(ラップして)上にパスする」という技術を使います。ここで登場するのが `InnerException`(内部例外)です。

例外クラスのコンストラクタには、以下のように「メッセージ」だけでなく「元の例外(InnerException)」を一緒に渡すオーバーロードが用意されています。

.net
‘ 【正しいアプローチ】InnerExceptionに元のエラーを宿してリレーする
Public Sub SaveUserData(userData As User)
Try
_dataAccess.ExecuteInsert(userData)

Catch ex As SqlClient.SqlException
‘ 💡 元のデータベース例外(ex)を抱え込んだまま、業務用の例外としてラップする
Throw New ApplicationException(“ユーザーデータの永続化処理でデータベースエラーが発生しました。”, ex)
End Sub
End Sub

このように実装すると、上位層でキャッチした例外から `ex.InnerException` を辿ることで、「どのデータベースエラーが原因で失敗したのか」を完璧に追跡できるようになります。

3. 実践!マルチレイヤーにおけるエラーログ出力の決定版

それでは、UI層、ビジネスロジック層(BLL)、データアクセス層(DAL)の3層構造を想定した、実戦でそのまま使えるコード例を見てみましょう。

開発現場でコピペしてベースにできるように、丁寧にコメントを入れています。

.net
‘ ==========================================================
‘ 【データアクセス層 (DAL)】一番下で泥臭くDBと戦う層
‘ ==========================================================
Public Class UserRepository
Public Sub Insert(name As String)
Try
‘ 意図的にエラーを発生させるシミュレーション(例:DB接続失敗)
Throw New System.Data.SqlClient.SqlException(“Aネットワーク接続タイムアウトが発生しました。”)

Catch ex As System.Data.SqlClient.SqlException
‘ DAL層固有の例外としてスローし直す
Throw New Exception(“データベースへの書き込みに失敗しました。”, ex)
End Try
End Sub
End Class

‘ ==========================================================
‘ 【業務ロジック層 (BLL)】ルールを司る真ん中の層
‘ ==========================================================
Public Class UserService
Private _repo As New UserRepository()

To RegisterNewUser(name As String)
Try
_repo.Insert(name)

Catch ex As Exception
‘ BLL層でさらにラップし、業務的な文脈を付与する
‘ ここで ex が InnerException として包まれる
Throw New InvalidOperationException(“新規ユーザーの登録処理に失敗しました。”, ex)
End Try
End Sub
End Class

‘ ==========================================================
‘ 【UI層 / プレゼンテーション層】ユーザーと対話する一番上の層
‘ ==========================================================
Public Class UserForm
Private _service As New UserService()

Private Sub btnSubmit_Click(sender As Object, e As EventArgs) Handles btnSubmit.Click
Try
_service.RegisterNewUser(“田中 太郎”)

Catch ex As Exception
‘ 🚨 ここで「再帰的」にすべての層のエラーを紐解いてログに出力する!
Dim errorDetails As String = GetDeepExceptionMessage(ex)

‘ 画面に優しく、ログには詳細を記録
MessageBox.Show(“エラーが発生しました。システム管理者にお問い合わせください。”, “エラー”, MessageBoxButtons.OK, MessageBoxIcon.Error)

‘ 実際の現場ではここでNLogやlog4netなどのロガーを使用します
Console.WriteLine(errorDetails)
End Try
End Sub

‘ ———————————————————-
‘ 匠の技:階層化された例外とスタックトレースをすべて掘り起こす関数
‘ ———————————————————-
Private Function GetDeepExceptionMessage(ex As Exception) As String
Dim sb As New System.Text.StringBuilder()
Dim currentEx As Exception = ex
Dim depth As Integer = 0

While currentEx IsNot Nothing
sb.AppendLine($”— [層 {depth}] —————————————-“)
sb.AppendLine($”例外型: {currentEx.GetType().FullName}”)
sb.AppendLine($”メッセージ: {currentEx.Message}”)
sb.AppendLine($”スタックトレース:”)
sb.AppendLine(currentEx.StackTrace)

‘ 次の内部例外(InnerException)へダイブする
currentEx = currentEx.InnerException
depth += 1
End While

Return sb.ToString()
End Function
End Class

4. このコードがもたらす圧倒的なメリット

上記の `GetDeepExceptionMessage` 関数を通したログ出力結果は、次のような美しい階層構造を描きます。

— [層 0] —————————————-
例外型: System.InvalidOperationException
メッセージ: 新規ユーザーの登録処理に失敗しました。
スタックトレース:
場所 UserService.RegisterNewUser(String name)
場所 UserForm.btnSubmit_Click(…)

— [層 1] —————————————-
例外型: System.Exception
メッセージ: データベースへの書き込みに失敗しました。
スタックトレース:
場所 UserRepository.Insert(String name)

— [層 2] —————————————-
例外型: System.Data.SqlClient.SqlException
メッセージ: Aネットワーク接続タイムアウトが発生しました。
スタックトレース:
場所 UserRepository.Insert(String name) (※実際の発生源)

これさえあれば、「どこでバグり、どういうルートをたどってエラーがここまで伝播したのか」が一目瞭然です。テスト環境や本番環境で不具合が起きた時も、迷うことなく最短ルートで原因箇所を特定できるようになります。

まとめ:ここをクリアすれば、VB.NETの基本はバッチリです!

今回は、マルチレイヤーアプリにおける `Exception.StackTrace` と `InnerException` の活用法について解説しました。

  • 例外を雑に握りつぶさない(元のエラーを隠さない)。
  • InnerExceptionを活用してエラーのバトン(文脈)をつなぐ
  • ループ処理を使って、奥深くにある本当の例外原因とスタックトレースを丸裸にする

この3つを意識するだけで、あなたの書くVB.NETコードの品質はプロフェッショナルな水準へと引き上げられます。エラーハンドリングは、単なる「保険」ではなく、「システムを健やかに保つための最高級のカルテ」です。

ここをクリアできれば、VB.NETの基礎、そしてオブジェクト指向の例外設計はもうバッチリですよ!
日々の開発ライフを、よりスマートで快適なものにしていきましょう。

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