第23章 WindowsフォームアプリからOracle Databaseを利用する
この章の目的
Section titled “この章の目的”この章では、WindowsフォームアプリからOracle Databaseに接続し、DBから取得した社員一覧を DataGridView に表示する方法を学習します。
第22章では、サンプルデータを List<Employee> として作成し、DataGridView に表示しました。
_employees = CreateSampleEmployees();
DisplayEmployees(_employees);この章では、サンプルデータではなく、Oracle Databaseの employees 表と departments 表からデータを取得します。
Oracle Database ↓EmployeeRepository ↓List<EmployeeListItem> ↓DataGridViewこれまで学習した内容が、この章でつながります。
OracleConnection → Oracle Databaseに接続する
OracleCommand → SQL文を実行する
OracleDataReader → SELECT結果を1行ずつ読み取る
EmployeeListItem → 画面表示用の1行分のデータを表す
List<EmployeeListItem> → 社員一覧をまとめて扱う
DataGridView → 一覧を表形式で表示するこの章では、WindowsフォームアプリからDBを利用する基本として、次の機能を作成します。
社員一覧表示社員名検索部署ID検索選択行の詳細表示登録・更新・削除は、この章では扱いません。
まずは、デスクトップアプリからDBのデータを取得して画面表示する流れを理解します。
挿絵・スクリーンショット案
Section titled “挿絵・スクリーンショット案”この章では、以下の画像を入れると理解しやすくなります。
図23-1 WindowsフォームアプリとOracle Databaseの接続イメージ図23-2 NuGetでOracle.ManagedDataAccess.Coreを追加する画面図23-3 フォームにDataGridViewと検索欄を配置した画面図23-4 Oracle DBから取得した社員一覧画面図23-5 社員名検索の結果画面図23-6 選択行の詳細表示画面図23-7 Form、Repository、Oracle DBの役割分担画像を挿入する場合は、次のように書けます。
画像ファイルは、images フォルダにまとめると管理しやすくなります。
CSharpText/ ├─ 23_winforms_oracle_database.md └─ images/ ├─ 23_winforms_oracle_overview.png ├─ 23_nuget_oracle_package.png ├─ 23_form_layout.png ├─ 23_employee_list.png └─ 23_employee_search.pngこの章でできるようになること
Section titled “この章でできるようになること”この章を終えると、次のことができるようになります。
- WindowsフォームアプリからOracle Databaseに接続できる
Oracle.ManagedDataAccess.CoreをWindowsフォームアプリに追加できるEmployeeListItemクラスを作成できるEmployeeRepositoryクラスを作成できるemployees表とdepartments表をJOINして社員一覧を取得できる- DBから取得したデータを
List<EmployeeListItem>として扱える DataGridViewにDBのデータを表示できる- 社員名で検索できる
- 部署IDで検索できる
- 選択行の社員情報を取得できる
- DB接続エラーが起きたときにメッセージを表示できる
- Form、Repository、Modelの役割分担を説明できる
本章で使用する環境
Section titled “本章で使用する環境”| 項目 | 内容 |
|---|---|
| 開発環境 | Visual Studio 2022 |
| プロジェクト種類 | Windows フォーム アプリ |
| 対象フレームワーク | .NET 8 |
| プロジェクト名 | Chapter23_WinFormsOracle |
| DB | Oracle Database |
| 接続ユーザー | pingt |
| 使用する表 | employees、departments |
| 追加するNuGetパッケージ | Oracle.ManagedDataAccess.Core |
作業前チェック
Section titled “作業前チェック”作業を始める前に、次の内容を確認してください。
- SQL*Plusで
pingtユーザーに接続できる -
employees表をSELECTできる -
departments表をSELECTできる - C#のコンソールアプリからOracle Databaseに接続できる
- Windowsフォームアプリを作成できる
-
DataGridViewにList<T>を表示できる -
OracleConnection、OracleCommand、OracleDataReaderの基本を理解している - 第22章の内容をGitに提出済みである
23-1 この章で作成するアプリ
Section titled “23-1 この章で作成するアプリ”この章では、Oracle Databaseから社員情報を取得し、Windowsフォームアプリに表示します。
画面には、次のような部品を配置します。
[一覧表示] [検索] [クリア] [選択行を表示]
社員名検索 [ ]部署ID [ ]
件数:0件
+-------------------------------------------------------------+| 社員ID | 社員名 | 部署名 | 入社日 | 給与 |+-------------------------------------------------------------+| 1001 | 山田二郎 | 総務 | 2001/04/01 | 500000 || 1002 | 佐藤昭夫 | 営業 | 2001/04/01 | 500000 |+-------------------------------------------------------------+このアプリでは、次の操作を行えます。
一覧表示 Oracle Databaseから社員一覧を取得してDataGridViewに表示する
社員名検索 入力された文字を含む社員を検索する
部署ID検索 指定された部署IDに所属する社員を検索する
選択行を表示 DataGridViewで選択した社員情報をMessageBoxに表示する23-2 アプリの構成
Section titled “23-2 アプリの構成”この章のアプリは、次の構成で作成します。
Form1 画面の操作を担当する
EmployeeRepository Oracle Databaseから社員データを取得する
EmployeeListItem DataGridViewに表示する1行分のデータを表す役割を分けると、次のようになります。
| クラス | 役割 |
|---|---|
Form1 | ボタンクリック、検索条件の取得、DataGridViewへの表示 |
EmployeeRepository | Oracle Databaseへの接続、SQL実行、データ取得 |
EmployeeListItem | 社員一覧に表示する1行分のデータ |
フォームにSQL文を直接書くこともできますが、この章では書きません。
Form → 画面操作を担当する
Repository → DBアクセスを担当するこの分け方は、Webアプリ編で行ったControllerとRepositoryの分離と似ています。
23-3 Windowsフォームアプリを作成する
Section titled “23-3 Windowsフォームアプリを作成する”Visual Studio 2022を起動し、次の手順でプロジェクトを作成します。
1. 「新しいプロジェクトの作成」をクリックする2. 「Windows フォーム アプリ」を選択する3. 「次へ」をクリックする4. プロジェクト名に Chapter23_WinFormsOracle と入力する5. 保存場所を確認する6. 「次へ」をクリックする7. フレームワークで .NET 8.0 を選択する8. 「作成」をクリックする作成直後に空のフォームが表示されます。
まずは実行し、空のフォームが表示されることを確認してください。
23-4 Oracle接続用パッケージを追加する
Section titled “23-4 Oracle接続用パッケージを追加する”WindowsフォームアプリからOracle Databaseに接続するために、NuGetパッケージを追加します。
使用するパッケージは、Webアプリ編やコンソールアプリ編と同じです。
Oracle.ManagedDataAccess.CoreVisual Studioで次の手順を行います。
1. ソリューションエクスプローラーでプロジェクト名を右クリックする2. 「NuGet パッケージの管理」をクリックする3. 「参照」タブを開く4. 検索欄に Oracle.ManagedDataAccess.Core と入力する5. Oracle.ManagedDataAccess.Core を選択する6. 「インストール」をクリックする7. 確認画面が出たら内容を確認して進めるOracle接続を行うクラスでは、次の using を使います。
using Oracle.ManagedDataAccess.Client;23-5 EmployeeListItemクラスを作成する
Section titled “23-5 EmployeeListItemクラスを作成する”まず、DataGridViewに表示する1行分のデータを表すクラスを作成します。
プロジェクトに EmployeeListItem.cs を追加します。
namespace Chapter23_WinFormsOracle{ public class EmployeeListItem { public int EmployeeId { get; set; } public string EmployeeName { get; set; } = ""; public string DepartmentName { get; set; } = ""; public DateTime HireDate { get; set; } public decimal? Salary { get; set; } }}このクラスは、社員一覧画面に表示する情報を持ちます。
| プロパティ | 意味 |
|---|---|
EmployeeId | 社員ID |
EmployeeName | 社員名 |
DepartmentName | 部署名 |
HireDate | 入社日 |
Salary | 給与 |
Salary は decimal? にしています。
public decimal? Salary { get; set; }これは、DBの salary がNULLになる可能性を考慮しているためです。
23-6 EmployeeRepositoryクラスを作成する
Section titled “23-6 EmployeeRepositoryクラスを作成する”EmployeeRepositoryの基本形
Section titled “EmployeeRepositoryの基本形”プロジェクトに EmployeeRepository.cs を追加します。
using Oracle.ManagedDataAccess.Client;
namespace Chapter23_WinFormsOracle{ public class EmployeeRepository { private readonly string _connectionString = "User Id=pingt;Password=oracle;Data Source=localhost:1521/XEPDB1;";
public List<EmployeeListItem> GetEmployeeListItems() { List<EmployeeListItem> employees = new List<EmployeeListItem>();
return employees; } }}このクラスは、Oracle Databaseから社員データを取得する役割を持ちます。
接続文字列について
Section titled “接続文字列について”接続文字列は、Oracle Databaseに接続するための情報です。
private readonly string _connectionString = "User Id=pingt;Password=oracle;Data Source=localhost:1521/XEPDB1;";この章では、学習を優先してクラス内に直接書きます。
実務では、接続文字列は設定ファイルや環境変数などで管理することが多いです。
23-7 社員一覧をDBから取得する
Section titled “23-7 社員一覧をDBから取得する”使用するSQL
Section titled “使用するSQL”社員一覧では、employees 表と departments 表をJOINし、部署名も一緒に取得します。
SELECT e.employee_id, e.employee_name, d.department_name, e.hiredate, e.salaryFROM employees e JOIN departments d ON e.department_id = d.department_idORDER BY e.employee_idGetEmployeeListItemsメソッド
Section titled “GetEmployeeListItemsメソッド”EmployeeRepository の GetEmployeeListItems メソッドを次のように作成します。
using Oracle.ManagedDataAccess.Client;
namespace Chapter23_WinFormsOracle{ public class EmployeeRepository { private readonly string _connectionString = "User Id=pingt;Password=oracle;Data Source=localhost:1521/XEPDB1;";
public List<EmployeeListItem> GetEmployeeListItems() { List<EmployeeListItem> employees = new List<EmployeeListItem>();
string sql = @" SELECT e.employee_id, e.employee_name, d.department_name, e.hiredate, e.salary FROM employees e JOIN departments d ON e.department_id = d.department_id ORDER BY e.employee_id";
using (OracleConnection connection = new OracleConnection(_connectionString)) { connection.Open();
using (OracleCommand command = new OracleCommand(sql, connection)) using (OracleDataReader reader = command.ExecuteReader()) { while (reader.Read()) { EmployeeListItem employee = CreateEmployeeListItem(reader);
employees.Add(employee); } } }
return employees; }
private EmployeeListItem CreateEmployeeListItem(OracleDataReader reader) { EmployeeListItem employee = new EmployeeListItem();
employee.EmployeeId = Convert.ToInt32(reader["employee_id"]); employee.EmployeeName = reader["employee_name"].ToString() ?? ""; employee.DepartmentName = reader["department_name"].ToString() ?? ""; employee.HireDate = Convert.ToDateTime(reader["hiredate"]); employee.Salary = reader["salary"] == DBNull.Value ? null : Convert.ToDecimal(reader["salary"]);
return employee; } }}GetEmployeeListItems の流れは次の通りです。
1. 空の List<EmployeeListItem> を作成する2. SQL文を用意する3. OracleConnectionでDBに接続する4. OracleCommandでSQL文を実行する5. OracleDataReaderで1行ずつ読み取る6. 1行分をEmployeeListItemに変換する7. Listに追加する8. 最後にListを返す第17章や第19章で学習した流れと同じです。
23-8 フォームにコントロールを配置する
Section titled “23-8 フォームにコントロールを配置する”フォームに次のコントロールを配置します。
| コントロール | Name | Text |
|---|---|---|
Button | btnLoad | 一覧表示 |
Button | btnSearch | 検索 |
Button | btnClear | クリア |
Button | btnShowDetail | 選択行を表示 |
Label | lblKeyword | 社員名検索 |
TextBox | txtKeyword | 空欄 |
Label | lblDepartmentId | 部署ID |
TextBox | txtDepartmentId | 空欄 |
Label | lblCount | 件数:0件 |
DataGridView | dgvEmployees | なし |
配置例:
[一覧表示] [検索] [クリア] [選択行を表示]
社員名検索 [ ]部署ID [ ]
件数:0件
+-----------------------------------------------------+| 社員ID | 社員名 | 部署名 | 入社日 | 給与 |+-----------------------------------------------------+DataGridViewの設定
Section titled “DataGridViewの設定”dgvEmployees には、次の設定を行います。
| プロパティ | 設定値 |
|---|---|
ReadOnly | True |
AllowUserToAddRows | False |
SelectionMode | FullRowSelect |
MultiSelect | False |
AutoSizeColumnsMode | Fill |
コードで設定する場合は、Form1.cs に次のメソッドを作成します。
private void SetupDataGridView(){ dgvEmployees.ReadOnly = true; dgvEmployees.AllowUserToAddRows = false; dgvEmployees.SelectionMode = DataGridViewSelectionMode.FullRowSelect; dgvEmployees.MultiSelect = false; dgvEmployees.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;}コンストラクターから呼び出します。
public Form1(){ InitializeComponent();
SetupDataGridView();}23-9 DBから取得した一覧をDataGridViewに表示する
Section titled “23-9 DBから取得した一覧をDataGridViewに表示する”Form1にフィールドを用意する
Section titled “Form1にフィールドを用意する”Form1.cs に、社員一覧を保持するフィールドを用意します。
namespace Chapter23_WinFormsOracle{ public partial class Form1 : Form { private List<EmployeeListItem> _employees = new List<EmployeeListItem>();
public Form1() { InitializeComponent();
SetupDataGridView(); } }}_employees には、DBから取得した全社員一覧を入れます。
DisplayEmployeesメソッド
Section titled “DisplayEmployeesメソッド”DataGridViewに社員一覧を表示するメソッドを作成します。
private void DisplayEmployees(List<EmployeeListItem> employees){ dgvEmployees.DataSource = null; dgvEmployees.DataSource = employees;
lblCount.Text = $"件数:{employees.Count}件";
SetColumnHeaders();}列見出しを日本語にする
Section titled “列見出しを日本語にする”DataGridViewの列見出しを日本語に変更します。
private void SetColumnHeaders(){ if (dgvEmployees.Columns["EmployeeId"] != null) { dgvEmployees.Columns["EmployeeId"].HeaderText = "社員ID"; }
if (dgvEmployees.Columns["EmployeeName"] != null) { dgvEmployees.Columns["EmployeeName"].HeaderText = "社員名"; }
if (dgvEmployees.Columns["DepartmentName"] != null) { dgvEmployees.Columns["DepartmentName"].HeaderText = "部署名"; }
if (dgvEmployees.Columns["HireDate"] != null) { dgvEmployees.Columns["HireDate"].HeaderText = "入社日"; }
if (dgvEmployees.Columns["Salary"] != null) { dgvEmployees.Columns["Salary"].HeaderText = "給与"; }}一覧表示ボタンの処理
Section titled “一覧表示ボタンの処理”btnLoad をダブルクリックし、クリックイベントを作成します。
private void btnLoad_Click(object sender, EventArgs e){ try { EmployeeRepository repository = new EmployeeRepository();
_employees = repository.GetEmployeeListItems();
DisplayEmployees(_employees); } catch (Oracle.ManagedDataAccess.Client.OracleException ex) { MessageBox.Show("Oracle Database処理中にエラーが発生しました。\n" + ex.Message); } catch (Exception ex) { MessageBox.Show("予期しないエラーが発生しました。\n" + ex.Message); }}実行し、一覧表示 ボタンをクリックしてください。
Oracle Databaseから取得した社員一覧がDataGridViewに表示されれば成功です。
usingを追加する場合
Section titled “usingを追加する場合”Form1.cs の先頭に次の using を追加しておくと、OracleException を短く書けます。
using Oracle.ManagedDataAccess.Client;その場合、catchは次のように書けます。
catch (OracleException ex){ MessageBox.Show("Oracle Database処理中にエラーが発生しました。\n" + ex.Message);}23-10 社員名で検索する
Section titled “23-10 社員名で検索する”Repositoryに検索メソッドを追加する
Section titled “Repositoryに検索メソッドを追加する”社員名で検索できるように、Repositoryにメソッドを追加します。
public List<EmployeeListItem> SearchEmployeeListItems(string? keyword, int? departmentId){ List<EmployeeListItem> employees = new List<EmployeeListItem>();
string sql = @" SELECT e.employee_id, e.employee_name, d.department_name, e.hiredate, e.salary FROM employees e JOIN departments d ON e.department_id = d.department_id WHERE (:keyword IS NULL OR e.employee_name LIKE :keyword) AND (:departmentId IS NULL OR e.department_id = :departmentId) ORDER BY e.employee_id";
using (OracleConnection connection = new OracleConnection(_connectionString)) { connection.Open();
using (OracleCommand command = new OracleCommand(sql, connection)) { command.BindByName = true;
if (string.IsNullOrWhiteSpace(keyword)) { command.Parameters.Add("keyword", OracleDbType.Varchar2).Value = DBNull.Value; } else { command.Parameters.Add("keyword", OracleDbType.Varchar2).Value = "%" + keyword + "%"; }
if (departmentId.HasValue) { command.Parameters.Add("departmentId", OracleDbType.Int32).Value = departmentId.Value; } else { command.Parameters.Add("departmentId", OracleDbType.Int32).Value = DBNull.Value; }
using (OracleDataReader reader = command.ExecuteReader()) { while (reader.Read()) { EmployeeListItem employee = CreateEmployeeListItem(reader);
employees.Add(employee); } } } }
return employees;}SQLパラメーターについて
Section titled “SQLパラメーターについて”SQLでは、検索条件を次のように書いています。
(:keyword IS NULL OR e.employee_name LIKE :keyword)keyword がNULLの場合は、社員名条件を無視します。
社員名が入力されている場合は、部分一致検索を行います。
command.Parameters.Add("keyword", OracleDbType.Varchar2).Value = "%" + keyword + "%";部署IDも同じ考え方です。
(:departmentId IS NULL OR e.department_id = :departmentId)部署IDが指定されている場合だけ、部署IDで絞り込みます。
23-11 検索ボタンの処理を作成する
Section titled “23-11 検索ボタンの処理を作成する”検索条件を取得する
Section titled “検索条件を取得する”btnSearch のクリックイベントを作成します。
private void btnSearch_Click(object sender, EventArgs e){ string keyword = txtKeyword.Text.Trim();
string departmentIdText = txtDepartmentId.Text.Trim();
int? departmentId = null;
if (!string.IsNullOrWhiteSpace(departmentIdText)) { if (!int.TryParse(departmentIdText, out int parsedDepartmentId)) { MessageBox.Show("部署IDは整数で入力してください。"); return; }
departmentId = parsedDepartmentId; }
try { EmployeeRepository repository = new EmployeeRepository();
List<EmployeeListItem> results = repository.SearchEmployeeListItems(keyword, departmentId);
DisplayEmployees(results);
if (results.Count == 0) { MessageBox.Show("該当する社員は見つかりませんでした。"); } } catch (Oracle.ManagedDataAccess.Client.OracleException ex) { MessageBox.Show("Oracle Database処理中にエラーが発生しました。\n" + ex.Message); } catch (Exception ex) { MessageBox.Show("予期しないエラーが発生しました。\n" + ex.Message); }}このコードでは、部署IDが入力されている場合だけ int に変換します。
空欄の場合は、departmentId は null のままです。
int? departmentId = null;クリアボタンの処理
Section titled “クリアボタンの処理”btnClear のクリックイベントを作成します。
private void btnClear_Click(object sender, EventArgs e){ txtKeyword.Clear(); txtDepartmentId.Clear();
DisplayEmployees(_employees);}検索後に全件表示へ戻すため、_employees に保持している全件データを再表示します。
検索ボタンでは、DBへ再度問い合わせています。
repository.SearchEmployeeListItems(keyword, departmentId);一方、クリアボタンでは、すでに取得済みの _employees を表示しています。
DisplayEmployees(_employees);必要に応じて、クリア時にもDBから再取得しても構いません。
_employees = repository.GetEmployeeListItems();DisplayEmployees(_employees);この章では、まず流れを分かりやすくするために、保持済みの全件データを再表示します。
23-12 選択行の詳細を表示する
Section titled “23-12 選択行の詳細を表示する”DataGridViewの選択行を取得する
Section titled “DataGridViewの選択行を取得する”第22章と同じように、選択行の DataBoundItem から EmployeeListItem を取得します。
btnShowDetail のクリックイベントを作成します。
private void btnShowDetail_Click(object sender, EventArgs e){ if (dgvEmployees.CurrentRow == null) { MessageBox.Show("社員を選択してください。"); return; }
EmployeeListItem? employee = dgvEmployees.CurrentRow.DataBoundItem as EmployeeListItem;
if (employee == null) { MessageBox.Show("社員情報を取得できませんでした。"); return; }
string salaryText = employee.Salary.HasValue ? $"{employee.Salary.Value}円" : "未設定";
string message = $"社員ID:{employee.EmployeeId}\n" + $"社員名:{employee.EmployeeName}\n" + $"部署名:{employee.DepartmentName}\n" + $"入社日:{employee.HireDate.ToString("yyyy/MM/dd")}\n" + $"給与:{salaryText}";
MessageBox.Show(message);}DataBoundItemの確認
Section titled “DataBoundItemの確認”DataGridView に設定している元データは、List<EmployeeListItem> です。
dgvEmployees.DataSource = employees;そのため、選択行の DataBoundItem は EmployeeListItem として取り出せます。
EmployeeListItem? employee = dgvEmployees.CurrentRow.DataBoundItem as EmployeeListItem;23-13 Form1.csの主要コード
Section titled “23-13 Form1.csの主要コード”ここまでの Form1.cs の主要部分は、次のようになります。
using Oracle.ManagedDataAccess.Client;
namespace Chapter23_WinFormsOracle{ public partial class Form1 : Form { private List<EmployeeListItem> _employees = new List<EmployeeListItem>();
public Form1() { InitializeComponent();
SetupDataGridView(); }
private void SetupDataGridView() { dgvEmployees.ReadOnly = true; dgvEmployees.AllowUserToAddRows = false; dgvEmployees.SelectionMode = DataGridViewSelectionMode.FullRowSelect; dgvEmployees.MultiSelect = false; dgvEmployees.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill; }
private void DisplayEmployees(List<EmployeeListItem> employees) { dgvEmployees.DataSource = null; dgvEmployees.DataSource = employees;
lblCount.Text = $"件数:{employees.Count}件";
SetColumnHeaders(); }
private void SetColumnHeaders() { if (dgvEmployees.Columns["EmployeeId"] != null) { dgvEmployees.Columns["EmployeeId"].HeaderText = "社員ID"; }
if (dgvEmployees.Columns["EmployeeName"] != null) { dgvEmployees.Columns["EmployeeName"].HeaderText = "社員名"; }
if (dgvEmployees.Columns["DepartmentName"] != null) { dgvEmployees.Columns["DepartmentName"].HeaderText = "部署名"; }
if (dgvEmployees.Columns["HireDate"] != null) { dgvEmployees.Columns["HireDate"].HeaderText = "入社日"; }
if (dgvEmployees.Columns["Salary"] != null) { dgvEmployees.Columns["Salary"].HeaderText = "給与"; } }
private void btnLoad_Click(object sender, EventArgs e) { try { EmployeeRepository repository = new EmployeeRepository();
_employees = repository.GetEmployeeListItems();
DisplayEmployees(_employees); } catch (OracleException ex) { MessageBox.Show("Oracle Database処理中にエラーが発生しました。\n" + ex.Message); } catch (Exception ex) { MessageBox.Show("予期しないエラーが発生しました。\n" + ex.Message); } }
private void btnSearch_Click(object sender, EventArgs e) { string keyword = txtKeyword.Text.Trim();
string departmentIdText = txtDepartmentId.Text.Trim();
int? departmentId = null;
if (!string.IsNullOrWhiteSpace(departmentIdText)) { if (!int.TryParse(departmentIdText, out int parsedDepartmentId)) { MessageBox.Show("部署IDは整数で入力してください。"); return; }
departmentId = parsedDepartmentId; }
try { EmployeeRepository repository = new EmployeeRepository();
List<EmployeeListItem> results = repository.SearchEmployeeListItems(keyword, departmentId);
DisplayEmployees(results);
if (results.Count == 0) { MessageBox.Show("該当する社員は見つかりませんでした。"); } } catch (OracleException ex) { MessageBox.Show("Oracle Database処理中にエラーが発生しました。\n" + ex.Message); } catch (Exception ex) { MessageBox.Show("予期しないエラーが発生しました。\n" + ex.Message); } }
private void btnClear_Click(object sender, EventArgs e) { txtKeyword.Clear(); txtDepartmentId.Clear();
DisplayEmployees(_employees); }
private void btnShowDetail_Click(object sender, EventArgs e) { if (dgvEmployees.CurrentRow == null) { MessageBox.Show("社員を選択してください。"); return; }
EmployeeListItem? employee = dgvEmployees.CurrentRow.DataBoundItem as EmployeeListItem;
if (employee == null) { MessageBox.Show("社員情報を取得できませんでした。"); return; }
string salaryText = employee.Salary.HasValue ? $"{employee.Salary.Value}円" : "未設定";
string message = $"社員ID:{employee.EmployeeId}\n" + $"社員名:{employee.EmployeeName}\n" + $"部署名:{employee.DepartmentName}\n" + $"入社日:{employee.HireDate.ToString("yyyy/MM/dd")}\n" + $"給与:{salaryText}";
MessageBox.Show(message); } }}23-14 この章の処理の流れ
Section titled “23-14 この章の処理の流れ”一覧表示の流れ
Section titled “一覧表示の流れ”1. 利用者が「一覧表示」ボタンをクリックする2. btnLoad_Click が実行される3. EmployeeRepository が作成される4. GetEmployeeListItems が呼ばれる5. Oracle Databaseへ接続する6. SELECT文を実行する7. OracleDataReaderで1行ずつ読み取る8. EmployeeListItemに変換する9. List<EmployeeListItem>としてFormに返す10. DataGridViewに表示する1. 利用者が社員名や部署IDを入力する2. 「検索」ボタンをクリックする3. btnSearch_Click が実行される4. 入力値を取得する5. 部署IDが入力されていれば整数に変換する6. Repositoryの検索メソッドを呼び出す7. SQLパラメーターを使ってSELECTする8. 結果をDataGridViewに表示する選択行表示の流れ
Section titled “選択行表示の流れ”1. 利用者がDataGridViewの行を選択する2. 「選択行を表示」ボタンをクリックする3. CurrentRowから選択行を取得する4. DataBoundItemからEmployeeListItemを取得する5. MessageBoxに社員情報を表示する23-15 Webアプリ編との比較
Section titled “23-15 Webアプリ編との比較”第19章では、MVCアプリからOracle Databaseに接続しました。
EmployeesController ↓EmployeeRepository ↓Oracle Database ↓Viewこの章では、Windowsフォームアプリから同じようにOracle Databaseに接続しています。
Form1 ↓EmployeeRepository ↓Oracle Database ↓DataGridView違うのは、表示する場所です。
| 種類 | 表示先 |
|---|---|
| Webアプリ | View、HTML、ブラウザー |
| Windowsフォームアプリ | Form、DataGridView |
一方で、DBアクセス部分の考え方は共通しています。
接続するSQLを実行するDataReaderで読み取るオブジェクトに変換するList<T>で返すつまり、C#アプリでは、アプリの種類が違っても、DBアクセスの基本的な流れは共通しています。
よくあるつまずき
Section titled “よくあるつまずき”| つまずき | 原因 | 対応 |
|---|---|---|
OracleConnection が見つからない | NuGetパッケージが入っていない | Oracle.ManagedDataAccess.Core を追加する |
OracleException が見つからない | using が不足している | using Oracle.ManagedDataAccess.Client; を追加する |
| DBに接続できない | Oracle Databaseが起動していない | SQL*Plusで接続確認する |
| 一覧が表示されない | DataSource を設定していない | DisplayEmployees を確認する |
| DataGridViewが空になる | SQL結果が0件 | SQL*Plusで同じSQLを実行する |
| 列見出しが英語のまま | SetColumnHeaders を呼んでいない | DisplayEmployees 内で呼ぶ |
| 検索しても全件出る | パラメーターがNULL扱いになっている | 入力値とSQL条件を確認する |
| 部署ID検索でエラーになる | 数字以外を入力している | int.TryParse で確認する |
| 給与NULLでエラーになる | DBNull.Value を確認していない | 変換前にNULL確認する |
| 選択行を取得できない | DataGridViewで行を選択していない | CurrentRow == null を確認する |
DataBoundItem がnullになる | DataSourceが正しくない | List<EmployeeListItem> を設定しているか確認する |
学んだことチェック
Section titled “学んだことチェック”次の項目について、自分で説明できるか確認してください。
- WindowsフォームアプリからOracle Databaseに接続できる
-
Oracle.ManagedDataAccess.Coreを追加できる -
EmployeeListItemの役割を説明できる -
EmployeeRepositoryの役割を説明できる -
employeesとdepartmentsをJOINして一覧データを取得できる -
OracleDataReaderからEmployeeListItemを作成できる -
List<EmployeeListItem>をDataGridViewに表示できる - DataGridViewの列見出しを日本語に変更できる
- 社員名検索を実装できる
- 部署ID検索を実装できる
- SQLパラメーターを使って検索条件を渡せる
- 選択行の
DataBoundItemから社員情報を取得できる - DB接続エラーが起きたときの確認ポイントを説明できる
研修の進め方によっては、隣の人または近くの人と説明確認を行います。
次の内容を、自分の言葉で説明してください。
- WindowsフォームアプリからDBを利用する場合、どのクラスがDBアクセスを担当していますか。
Form1にSQL文を直接書かない理由は何ですか。EmployeeListItemは何を表すクラスですか。DataGridView.DataSourceには何を設定していますか。- DBのNULLをC#で扱うとき、何を確認しますか。
- 社員名検索でSQLパラメーターを使う理由は何ですか。
CurrentRowとDataBoundItemはそれぞれ何を表していますか。- Webアプリ編とWindowsフォームアプリ編で、DBアクセス処理の共通点は何ですか。
この章の演習課題に取り組みます。
制限時間は 100分 です。
時間内にすべて完成しなくても構いません。
できたところまでを保存し、Gitに提出してください。
課題23-1 Windowsフォームアプリを作成する
Section titled “課題23-1 Windowsフォームアプリを作成する”Windowsフォームアプリのプロジェクトを作成してください。
条件:
- プロジェクト名は
Chapter23_WinFormsOracleとする - フレームワークは
.NET 8.0を選択する - 初期状態で実行し、フォームが表示されることを確認する
課題23-2 Oracle接続用パッケージを追加する
Section titled “課題23-2 Oracle接続用パッケージを追加する”NuGetで Oracle.ManagedDataAccess.Core を追加してください。
条件:
- プロジェクトにパッケージを追加する
using Oracle.ManagedDataAccess.Client;を使える状態にする
課題23-3 EmployeeListItemクラスを作成する
Section titled “課題23-3 EmployeeListItemクラスを作成する”社員一覧表示用の EmployeeListItem クラスを作成してください。
プロパティ:
| プロパティ名 | 型 |
|---|---|
EmployeeId | int |
EmployeeName | string |
DepartmentName | string |
HireDate | DateTime |
Salary | decimal? |
条件:
EmployeeListItem.csを作成するSalaryはdecimal?にする- 文字列プロパティには初期値
""を設定する
課題23-4 EmployeeRepositoryを作成する
Section titled “課題23-4 EmployeeRepositoryを作成する”Oracle Databaseから社員一覧を取得する EmployeeRepository クラスを作成してください。
条件:
EmployeeRepository.csを作成する- 接続文字列をフィールドとして定義する
GetEmployeeListItemsメソッドを作成するemployeesとdepartmentsをJOINする- SELECT結果を
EmployeeListItemに変換する List<EmployeeListItem>として返す
課題23-5 DataGridViewに社員一覧を表示する
Section titled “課題23-5 DataGridViewに社員一覧を表示する”DBから取得した社員一覧をDataGridViewに表示してください。
条件:
- フォームに
dgvEmployeesを配置する btnLoadを配置するlblCountを配置するbtnLoad_ClickでRepositoryを呼び出すDisplayEmployeesメソッドでDataGridViewに表示する- 件数を表示する
課題23-6 列見出しを日本語にする
Section titled “課題23-6 列見出しを日本語にする”DataGridViewの列見出しを日本語に変更してください。
条件:
EmployeeIdを社員IDにするEmployeeNameを社員名にするDepartmentNameを部署名にするHireDateを入社日にするSalaryを給与にする
課題23-7 社員名検索を作成する
Section titled “課題23-7 社員名検索を作成する”社員名で部分一致検索できるようにしてください。
条件:
txtKeywordを追加するbtnSearchを追加する- Repositoryに検索メソッドを追加する
- SQLで
LIKE :keywordを使う - SQLパラメーターを使う
- 検索結果をDataGridViewに表示する
課題23-8 部署ID検索を追加する
Section titled “課題23-8 部署ID検索を追加する”部署IDで検索できるようにしてください。
条件:
txtDepartmentIdを追加する- 部署IDが入力されている場合は整数チェックする
- Repositoryに部署ID条件を追加する
- 社員名と部署IDの両方で検索できるようにする
課題23-9 クリアボタンを作成する
Section titled “課題23-9 クリアボタンを作成する”検索条件をクリアし、全件表示に戻してください。
条件:
btnClearを追加するtxtKeyword.Clear()を使うtxtDepartmentId.Clear()を使う- 全件一覧をDataGridViewに表示する
課題23-10 選択行の社員情報を表示する
Section titled “課題23-10 選択行の社員情報を表示する”DataGridViewで選択した社員情報を、メッセージボックスで表示してください。
条件:
btnShowDetailを追加するCurrentRowを確認するDataBoundItemからEmployeeListItemを取得する- 何も選択されていない場合はメッセージを表示する
課題23-11 SQL*Plusで同じSQLを確認する
Section titled “課題23-11 SQL*Plusで同じSQLを確認する”Windowsフォームアプリで使用しているSQLを、SQL*Plusでも実行してください。
条件:
- 社員一覧用SQLをSQL*Plusで実行する
- 社員名検索用SQLをSQL*Plusで実行する
- 部署ID検索用SQLをSQL*Plusで実行する
- アプリ画面の結果と一致することを確認する
Gitへの提出
Section titled “Gitへの提出”課題が終わったら、できたところまでをGitに提出します。
git statusgit add .git commit -m "Chapter23 WinFormsからOracle Databaseを利用"git pushGitの操作でエラーが出た場合は、自己判断で同じ操作を繰り返さず、講師に確認してください。
提出前チェックリスト
Section titled “提出前チェックリスト”- Windowsフォームアプリを作成できている
-
Oracle.ManagedDataAccess.Coreを追加している -
EmployeeListItemクラスを作成している -
EmployeeRepositoryクラスを作成している -
employeesとdepartmentsをJOINして取得している - DBのNULLを
DBNull.Valueで確認している -
List<EmployeeListItem>をDataGridViewに表示している - DataGridViewの列見出しを日本語にしている
- 社員名検索ができている
- 部署ID検索ができている
- SQLパラメーターを使っている
- 選択行の社員情報を表示できている
- DB接続エラー時にメッセージを表示している
- インデントが整っている
- Gitにcommitしている
- Gitにpushしている
この章のまとめ
Section titled “この章のまとめ”この章では、WindowsフォームアプリからOracle Databaseを利用する方法を学習しました。
この章で学んだ主な内容は次の通りです。
- WindowsフォームアプリからOracle Databaseに接続できる
- Oracle接続には
Oracle.ManagedDataAccess.Coreを利用する - DBアクセス処理は
EmployeeRepositoryに分けると見通しがよくなる employeesとdepartmentsをJOINして、社員一覧に部署名を表示できる- DBから取得した1行分のデータを
EmployeeListItemに変換できる - 複数行の結果を
List<EmployeeListItem>として扱える DataGridView.DataSourceにList<EmployeeListItem>を設定すると、一覧表示できる- DataGridViewの列見出しを日本語に変更できる
- 社員名や部署IDで検索できる
- 検索条件はSQLパラメーターで渡す
- DBのNULLは
DBNull.Valueとして確認する必要がある - 選択行の
DataBoundItemから、元のEmployeeListItemを取得できる - Webアプリとデスクトップアプリでは表示方法は違うが、DBアクセスの基本的な流れは共通している
次章では、Windowsフォームアプリで登録・更新・削除の流れを体験する ことを学習します。
ただし、Webアプリ編と同じく、既存データを壊さないために9000番台の社員IDを研修用データとして扱い、INSERT / UPDATE / DELETE の基本的な流れを確認します。