# 通知設計書 24-SmtpClient.SendCompleted

## 概要

本ドキュメントは、System.Net.Mail.SmtpClientクラスにおけるSendCompletedイベントの設計仕様を定義する。SendCompletedは、非同期メール送信操作（SendAsync）が完了した際に発生するイベントであり、送信成功、失敗、またはキャンセルの状態をアプリケーションに通知する。

### 本通知の処理概要

SendCompletedイベントは、SmtpClient.SendAsyncメソッドによる非同期メール送信操作の完了を通知するための標準的な.NETイベントパターンの実装である。このイベントを通じて、アプリケーションは送信結果を非同期的に受け取り、UIの更新やログ記録などの後続処理を実行できる。

**業務上の目的・背景**：メール送信は時間のかかるI/O操作であり、UIスレッドをブロックせずに実行することが重要である。SendCompletedイベントは、非同期送信操作の完了を通知し、アプリケーションが送信結果に基づいた処理（成功通知、エラー処理、リトライロジックなど）を実行できるようにする。これにより、ユーザーエクスペリエンスを損なうことなく、バックグラウンドでのメール送信を実現できる。

**通知の送信タイミング**：SendAsyncメソッドによる非同期メール送信操作が完了した時点（成功、失敗、キャンセルのいずれの場合も）で発生する。具体的には、SendAsyncInternal内部メソッドのfinally句でOnSendCompletedが呼び出される。

**通知の受信者**：SendCompletedイベントにイベントハンドラを登録したアプリケーションコード。デリゲート型はSendCompletedEventHandler（AsyncCompletedEventArgsを受け取る）である。

**通知内容の概要**：AsyncCompletedEventArgsオブジェクトが渡され、以下の情報を含む：
- Error: 送信中に発生した例外（成功時はnull）
- Cancelled: キャンセルされたかどうか
- UserState: SendAsync呼び出し時に渡されたユーザー定義オブジェクト

**期待されるアクション**：受信者は送信結果を確認し、適切な処理を実行する。成功時はUI更新や確認メッセージ表示、失敗時はエラーログ記録やリトライ処理、キャンセル時はクリーンアップ処理などを行う。

## 通知種別

アプリケーションイベント通知（.NETイベントパターン）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 非同期（SendAsync完了時にイベント発火） |
| 優先度 | 中（アプリケーションレベルの通知） |
| リトライ | 無（イベントは一度のみ発火） |

### 送信先決定ロジック

SendCompletedイベントに登録されているすべてのイベントハンドラに対してイベントが発火される。イベントハンドラは呼び出し元スレッドのコンテキストで同期的に実行される。

## 通知テンプレート

### イベント引数

```csharp
public class AsyncCompletedEventArgs : EventArgs
{
    public Exception Error { get; }      // 発生した例外（成功時はnull）
    public bool Cancelled { get; }       // キャンセルされたかどうか
    public object UserState { get; }     // ユーザー定義オブジェクト
}
```

### デリゲート定義

```csharp
public delegate void SendCompletedEventHandler(object sender, AsyncCompletedEventArgs e);
```

### 本文テンプレート

```
イベント: SendCompleted
送信元: SmtpClientインスタンス
結果:
  - 成功: Error = null, Cancelled = false
  - 失敗: Error = {Exception}, Cancelled = false
  - キャンセル: Error = null, Cancelled = true
ユーザー状態: {UserState}
```

### 添付ファイル

なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| sender | イベントを発生させたSmtpClientインスタンス | this | Yes |
| e.Error | 送信中に発生した例外 | SendAsyncInternalのキャッチ例外 | No |
| e.Cancelled | 送信がキャンセルされたか | SendAsyncCancelによるキャンセル検出 | Yes |
| e.UserState | ユーザー定義オブジェクト | SendAsync呼び出し時のuserTokenパラメータ | No |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| 非同期操作完了 | SendAsync完了 | invokeSendCompleted=true | 正常完了時 |
| 非同期操作失敗 | 送信中例外発生 | invokeSendCompleted=true | SmtpException等発生時 |
| 非同期操作キャンセル | SendAsyncCancel呼び出し | invokeSendCompleted=true | ユーザーキャンセル時 |
| タイムアウト | Timeout経過 | invokeSendCompleted=true | 送信タイムアウト時 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| 同期送信 | Send/SendMailAsyncメソッドではイベントは発火しない |
| synchronous=true | 引数検証などの同期的エラーではイベントは発火しない |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[SendAsync呼び出し] --> B[SendAsyncInternal開始]
    B --> C[引数検証]
    C -->|検証失敗| D[例外スロー（イベントなし）]
    C -->|検証成功| E[非同期送信開始]
    E --> F{送信結果}
    F -->|成功| G[exception = null]
    F -->|失敗| H[exception = 発生例外]
    F -->|キャンセル| I[canceled = true]
    G --> J[finally句]
    H --> J
    I --> J
    J --> K{invokeSendCompleted && !synchronous}
    K -->|Yes| L[AsyncCompletedEventArgs生成]
    K -->|No| M[イベントなしで終了]
    L --> N[OnSendCompleted呼び出し]
    N --> O[SendCompleted?.Invoke]
    O --> P[ユーザーハンドラ実行]
```

## データベース参照・更新仕様

### 参照テーブル一覧

該当なし（メール送信はSMTPプロトコル経由であり、データベースは使用しない）

### 更新テーブル一覧

該当なし

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| SmtpException | SMTP通信エラー | Error プロパティで例外を確認、リトライまたはエラー報告 |
| SmtpFailedRecipientException | 宛先拒否 | 失敗した宛先を確認、代替アドレスまたはエラー報告 |
| OperationCanceledException | キャンセル | Cancelledプロパティで確認、クリーンアップ処理 |
| InvalidOperationException | 同時送信 | 前の送信完了を待機 |
| ObjectDisposedException | Dispose済み | 新しいSmtpClientインスタンスを作成 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | アプリケーション実装に依存 |
| リトライ間隔 | アプリケーション実装に依存 |
| リトライ対象エラー | SmtpException（一時的なエラーの場合） |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 制限なし | イベントは操作完了時に1回のみ発火 |

### 配信時間帯

制限なし（操作完了時即座に配信）

## セキュリティ考慮事項

- SendCompletedイベントハンドラ内で例外が発生した場合、アプリケーションがクラッシュする可能性がある
- UserStateオブジェクトに機密情報を含めないこと
- イベントハンドラはSmtpClient内部ロックを保持しない状態で呼び出される

## 備考

- SendAsyncは従来の非同期パターン（EAP: Event-based Asynchronous Pattern）を使用
- SendMailAsyncはTask-based Asynchronous Pattern（TAP）を使用し、SendCompletedイベントは発火しない
- 同時に複数のSendAsync呼び出しは許可されない（InvalidOperationExceptionがスローされる）

---

## コードリーディングガイド

本通知を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

イベントハンドラとイベント引数の定義を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | SmtpClient.cs | `src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpClient.cs` | 行21: SendCompletedEventHandlerデリゲート定義 |
| 1-2 | SmtpClient.cs | `src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpClient.cs` | 行57: SendCompletedイベント宣言 |

**読解のコツ**: AsyncCompletedEventArgsはSystem.ComponentModel名前空間で定義されており、標準的な.NET非同期パターンに従っている。

#### Step 2: イベント発火メカニズムを理解する

OnSendCompletedメソッドとその呼び出し元を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | SmtpClient.cs | `src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpClient.cs` | 行380-383: OnSendCompleted実装 |
| 2-2 | SmtpClient.cs | `src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpClient.cs` | 行574-579: finally句でのOnSendCompleted呼び出し |

**主要処理フロー**:
- **行380-383**: `OnSendCompleted` - nullチェック付きでSendCompleted?.Invoke(this, e)を呼び出し
- **行575-578**: invokeSendCompletedかつ!synchronousの場合にのみイベント発火

#### Step 3: 非同期送信フローを理解する

SendAsyncとSendAsyncInternal全体の流れを追う。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | SmtpClient.cs | `src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpClient.cs` | 行591-605: SendAsync public メソッド |
| 3-2 | SmtpClient.cs | `src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpClient.cs` | 行410-583: SendAsyncInternal実装 |
| 3-3 | SmtpClient.cs | `src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpClient.cs` | 行621-635: SendAsyncCancel実装 |

**主要処理フロー**:
- **行591-605**: SendAsync - SendAsyncInternalをinvokeSendCompleted=trueで呼び出し
- **行436**: Interlocked.Exchange(ref _inCall, true)で同時実行防止
- **行537-568**: 例外処理とProcessException
- **行569-579**: finally句でイベント発火

### プログラム呼び出し階層図

```
SendAsync (行591)
    |
    v
SendAsyncInternal<AsyncReadWriteAdapter> (行410)
    |
    ├─ 引数検証 (行448-485)
    |      └─ 同期的エラー → 例外スロー（イベントなし）
    |
    ├─ EnsureConnection (行516)
    |
    ├─ _transport.SendMailAsync (行520-521)
    |
    ├─ message.SendAsync (行525)
    |
    └─ finally (行569)
           |
           ├─ _inCall = false (行571)
           |
           └─ if (invokeSendCompleted && !synchronous) (行575)
                  |
                  └─ OnSendCompleted (行577-578)
                         |
                         v
                     SendCompleted?.Invoke(this, eventArgs) (行382)
                         |
                         v
                     ユーザーハンドラ実行
```

### データフロー図

```
[入力]                      [処理]                              [出力]

SendAsync(message,     ───▶  SendAsyncInternal           ───▶  非同期送信開始
  userToken)                  (行410-583)
    |                                                            |
    v                                                            v
MailMessage,           ───▶  SMTP送信処理               ───▶  送信結果
userToken                     (EnsureConnection,                  |
                              SendMailAsync)                      v
                                   |                         exception or
                                   v                         success
                              finally句 (行569)
                                   |
                                   v
                              AsyncCompletedEventArgs生成 (行577)
                                   |
                                   v
                              OnSendCompleted (行380)
                                   |
                                   v
                              イベントハンドラ実行
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| SmtpClient.cs | `src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpClient.cs` | ソース | SmtpClient実装、SendCompletedイベント定義（行21, 57, 380-383, 574-579） |
| SmtpTransport.cs | `src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpTransport.cs` | ソース | SMTP通信処理 |
| MailMessage.cs | `src/libraries/System.Net.Mail/src/System/Net/Mail/MailMessage.cs` | ソース | メールメッセージ定義 |
| System.Net.Mail.csproj | `src/libraries/System.Net.Mail/src/System.Net.Mail.csproj` | プロジェクト | プロジェクト設定 |
