情報応用演習Ⅰ(2024)

【T2e】Entity Framework Core を用いたデータベースへのアクセス(4/6)

プロジェクトタイプC#コンソールアプリ※
プロジェクト名T2e
ソリューション名PIT2
ターゲットフレームワーク.NET 8.0(長期的なサポート)
最上位レベルのステートメントを使用しない使用する(チェックオフ)

※ 「コンソールアプリ(.NET Framework)」ではないので注意せよ!

注意
  • 本ページの作業内容は 前のページまでの続き になっていることに注意せよ.
    • 先に前のページまでをすべて読み,指示されている作業を済ませてから本ページを読むこと.
    • プロジェクトの作成作業についてはO/Rマッピングの必要性を参照せよ.

2e-4. Entity Framework Core の基礎

Entity Framework Coreには主に以下の2通りの使い方がある(_).

  • Database First (でーたべーす・ふぁーすと)
    • 既存のデータベースからクラスの定義を作る使い方
  • Code First (こーど・ふぁーすと)
    • 既存のクラス定義からデータベースのテーブル定義を作る使い方
Database First と Code First

今回はあらかじめ定義済みの「従業員データベース」にアクセスするのでDatabase Firstの使い方を採用する

では,C#の「コンソールアプリ」タイプのプロジェクトを作成しよう(「コンソールアプリ(.NET Framework)」ではないので注意せよ). 上表に示すように,プロジェクト名はT2eとする.ターゲットフレームワークとして 「.NET 8.0(長期的なサポート)」を選択し,「最上位レベルのステートメントを使用しない」には チェックが入っていない ことを確認しよう.

プロジェクトを作成したら,NuGetを使って以下のパッケージを検索してインストールしよう.バージョンが選択可能な場合は 「 最新の安定板 」を選択するのを忘れないようにしよう.

パッケージ名本校執筆時1の安定版
Microsoft.EntityFrameworkCore.Design8.0.3
Npgsql.EntityFrameworkCore.PostgreSQL8.0.2
Npgsql.EntityFrameworkCore.PostgreSQL.Design1.1.0
必要なパッケージのインストール

次にデータベースの定義から,それらをプログラム上で使用できるようにするためのクラスを生成する. このためにはコマンドラインでの操作が必要であるが,Visual Studio には PowerShell のコマンドラインターミナルが統合されているので 以降はそれを使用して操作することにしよう.Visual Studio の「表示」メニュー→「Terminal」(もしくは「ターミナル」)をクリックする(_). すると_に示すようなコマンドラインターミナル2が表示されるはずである.これは前出のPowerShellと同等のものである.

Visual Studio のターミナルの表示とプロジェクトの事前ビルド

まず以降の作業を行う前に,プロジェクトを一度ビルドしておこう.このためにはソリューションエクスプローラーで T2e プロジェクトを右クリック→「ビルド」をクリックする(_).「出力」ウィンドウに「ビルド: 1 正常終了、...」などと表示されれば問題なくビルドができている(_).

以上を確認したら,コマンドラインターミナル2_に示すコマンドを実行しよう. なおこのdotnet ~というコマンドを実行する際は, カレントディレクトリを操作対象のプロジェクトのプロジェクトフォルダに変更する必要がある .今回の場合はあらかじめcd プロジェクト名を実行しておく必要がある.カレントディレクトリの概念や確認方法,cdコマンドの使用方法などは 2年次の情報基礎演習で学んでいるはず であるためここでの説明は割愛する.

なお,今回のソリューションに含まれるプロジェクトに1つでもコンパイルエラーがあるとこのコマンドは失敗することがある.このコマンドを実行する際は一つもコンパイルエラーがないことを確認しておくこと.

クラス定義を生成するコマンド
PS> dotnet ef dbcontext scaffold "Host=localhost;Port=5432;Database=SampleDB_hr;Username=hr_user;Password=n1z3Lo9AQap3" Npgsql.EntityFrameworkCore.PostgreSQL

_のコマンド「dotnet ef dbcontext scaffold」は,既存のデータベースのテーブル定義から各テーブルに対応するクラス定義を生成するためのコマンドである(参考:Reverse Engineering - EF Core - Microsoft Docs). コマンドライン引数として,接続文字列とデータベースプロバイダーを指定する必要がある.今回は接続文字列として,前節で使用したのと全く同じ接続文字列を使用している.データベースプロバイダーとはデータベースとの接続を担うプログラム部品,つまりはデータベースドライバの指定であるが今回は先ほどインストールした Npgsql.EntityFrameworkCore.PostgreSQL を指定している.

_を実行すると,コマンドラインターミナルに_①のように表示され, このプロジェクトに_②のように複数の .cs ファイルが追加される.

Scaffold-DbContextコマンドの実行結果

これらの .cs ファイルは,主にデータベース SampleDB_hr に含まれる7個のテーブルに対応するクラス定義である. ためしに TblJugyoin.cs を開いてみよう._のようなクラス定義が含まれていることがわかるだろう.

自動生成された従業員クラス

おおむねリスト2e-2-1で示したようなクラスになっていることがわかるだろう. ちなみにテーブル名とクラス名の対応は_のようになっている.単純な命名規則の変換であることがわかるだろう.

テーブル名とクラス名の対応
テーブル名クラス名備考
tbl_bumonTblBumon部門テーブル
tbl_chiikiTblChiiki地域テーブル
tbl_jigyoshoTblJigyosho事業所テーブル
tbl_jugyoinTblJugyoin従業員テーブル
tbl_shokurekiTblShokureki職歴テーブル
tbl_shokushuTblShokushu職種テーブル
tbl_todofukenTblTodofuken都道府県テーブル

また_に示すように,これらのほかにもうひとつ, Entity Framework Core においてはデータコンテキストクラスと呼ばれる,そのデータベースに含まれているすべてのテーブルのハブとなるクラスも生成されている.たいていの場合はデータベース名Contextという名前のクラスとして生成される.今回の場合はSampleDbHrContextクラスであり,その定義は SampleDbHrContext.cs に含まれているのでこれを開いてみよう(_).

SampleDB_hrデータベースに対応するDbContext

これがSampleDB_hr用のデータコンテキストクラスにあたる.このクラスには各テーブルに対応するDbSet型のプロパティ(強調部分)が含まれており,データベースへの読み書きはこれらのプロパティを通じて行う(_).

データコンテキストクラスを使ったデータベースの読み書き

実際にこれらのクラスを使ってデータベースにアクセスしてみよう.Entity Framework Core では, Microsoft.EntityFrameworkCore名前空間のクラスが必要なので,コードの冒頭に_のような usingディレクティブを追記する必要がある.

Entity Framework Core の使用に必要なusingディレクティブ
1
using Microsoft.EntityFrameworkCore;

では Program.cs に_に示す内容を記述しよう.

Program.csの内容
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
using Microsoft.EntityFrameworkCore;
using T2e;

// ① データコンテキストクラスのインスタンスの生成(データベースとの接続を開始)
var context = new SampleDbHrContext();

// ② クエリの生成(LINQを使う)
var query = from j in context.TblJugyoins
            orderby j.JugyoinNo ascending
            select j;

// ③ 結果の読み取り
foreach (var jugyoin in query) 
{
    Console.WriteLine($"jugyoin_no = {jugyoin.JugyoinNo}, sei = {jugyoin.Sei}, mei = {jugyoin.Mei}");
}//foreach

前節のリスト2d-4-3に比べるとかなりシンプルになっていることがわかるだろう. しかし実行内容はリスト2d-4-3と全く同じである. ここまで書けたら実行してみよう.実行結果は_のようになる.

jugyoin_no = 100, sei = 孫, mei = 正義
jugyoin_no = 101, sei = 荒木, mei = 飛呂彦
jugyoin_no = 106, sei = 小沢, mei = 一郎
jugyoin_no = 107, sei = 東藤, mei = 大輝
jugyoin_no = 108, sei = 宮沢, mei = 賢治
jugyoin_no = 109, sei = 御坂, mei = 美琴
jugyoin_no = 113, sei = 安倍, mei = 晋三
jugyoin_no = 123, sei = 高橋, mei = 凜
jugyoin_no = 124, sei = 滝野, mei = 智
jugyoin_no = 143, sei = 浜田, mei = 雅功
jugyoin_no = 144, sei = 毛受, mei = 綾乃
jugyoin_no = 146, sei = 鳩山, mei = 一郎
jugyoin_no = 149, sei = 渡辺, mei = 達也
jugyoin_no = 177, sei = 斉藤, mei = 豊
jugyoin_no = 178, sei = 太宰, mei = 治
jugyoin_no = 179, sei = 阿万音, mei = 鈴羽
jugyoin_no = 200, sei = 中村, mei = 一郎
jugyoin_no = 201, sei = 岡部, mei = 倫太郎
jugyoin_no = 202, sei = 白井, mei = 黒子
jugyoin_no = 203, sei = 宮迫, mei = 博之
jugyoin_no = 204, sei = 池田, mei = 功
jugyoin_no = 205, sei = 橋田, mei = 至
jugyoin_no = 206, sei = 福沢, mei = 諭吉
実行結果

_の①~③で何をしているのかを説明しておこう.

①では今回のデータコンテキストクラスにあたるSampleDbHrContextクラスの変数を作成している. この時点でPostgreSQLとの通信が開始されている.

②ではLINQを使って従業員テーブル(.TblJugyoinプロパティ)から含まれている全てのデータを取得している. この場合は全く絞り込みをしていないので,仮にLINQを使わずに var query = context.TblJugyoins と書いても全く同じであるが, 後の説明のためにあえてLINQを使っている.

③では②で取得されたデータの処理を行っている.LINQを使っているので,ここは単なる foreach 文で実装できる. ここでは取得された従業員の従業員番号(.JugyoinNo),姓(.Sei),名(.Mei)を表示している.

実際にPostgreSQL側で実行されたSQL文も確認しておこう.プログラムを実行直後に,コマンドラインターミナル2_を実行すると直前に実行されたSQL文を確認することができる(長いのでコピペ推奨).

直前に実行されたSQL文の取得コマンド
PS> Get-Eventlog -LogName Application -Source PostgreSQL | where {$_.Message.Contains("execute")} | select -ExpandProperty Message -First 1

_を実行すると_のような出力が得られるはずである. 従業員テーブル(tbl_jugyoin)に対してSELECT文が実行されていることが分かる.

直前のSQL文の取得コマンドの実行結果
2022-01-12 15:49:06.944 JST [15604] LOG:  execute <unnamed>: SELECT t.jugyoin_no, t.buai, t.bumon_no, t.email, t.first_name, t.kanrisha_no, t.kyuyo, t.last_name, t.mei, t.phone_no, t.sei, t.shokushu_no, t.shugyo_bi
        FROM tbl_jugyoin AS t
        ORDER BY t.jugyoin_no

試しに Program.cs に_に示す内容を追記して絞り込みを指定し,またソート順序も変更してみよう.

Program.csの内容
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
using Microsoft.EntityFrameworkCore;
using T2e;

// ① データコンテキストクラスのインスタンスの生成(データベースとの接続を開始)
var context = new SampleDbHrContext();

// ② クエリの生成(LINQを使う)
var query = from j in context.TblJugyoins
            where j.Kyuyo >= 800000        // 給与80万円以上を取得
            orderby j.ShugyoBi descending  // 就業日の降順
            select j;

// ③ 結果の読み取り
foreach (var jugyoin in query) 
{
    Console.WriteLine($"jugyoin_no = {jugyoin.JugyoinNo}, sei = {jugyoin.Sei}, mei = {jugyoin.Mei}");
    Console.WriteLine($"     kyuyo = {jugyoin.Kyuyo,10}, shugyobi = {jugyoin.ShugyoBi:yyyy-MM-dd}");
}//foreach

_の実行結果は_のようになる. 指定した通りの絞り込みとソートが行われていることが分かる.

jugyoin_no = 149, sei = 渡辺, mei = 達也
     kyuyo =    1050000, shugyobi = 2012-01-29
jugyoin_no = 177, sei = 斉藤, mei = 豊
     kyuyo =     840000, shugyobi = 2010-04-23
jugyoin_no = 146, sei = 鳩山, mei = 一郎
     kyuyo =    1350000, shugyobi = 2009-01-05
jugyoin_no = 201, sei = 岡部, mei = 倫太郎
     kyuyo =    1300000, shugyobi = 2008-02-17
jugyoin_no = 108, sei = 宮沢, mei = 賢治
     kyuyo =    1200000, shugyobi = 2006-08-17
jugyoin_no = 109, sei = 御坂, mei = 美琴
     kyuyo =     900000, shugyobi = 2006-08-16
jugyoin_no = 206, sei = 福沢, mei = 諭吉
     kyuyo =     830000, shugyobi = 2006-06-07
jugyoin_no = 204, sei = 池田, mei = 功
     kyuyo =    1000000, shugyobi = 2006-06-07
jugyoin_no = 205, sei = 橋田, mei = 至
     kyuyo =    1200000, shugyobi = 2006-06-07
jugyoin_no = 101, sei = 荒木, mei = 飛呂彦
     kyuyo =    1700000, shugyobi = 2001-09-21
jugyoin_no = 100, sei = 孫, mei = 正義
     kyuyo =    2400000, shugyobi = 1999-06-17
実行結果

また_のコマンドを実行して直前に実行されたSQL文も確認しておこう. コマンドラインターミナルをさきほどから開いたままであれば,コマンドを打ち直さずとも↑キーを押すことで,今まで実行したコマンドの履歴をたどって実行することができる (表示が崩れる場合は何度かCtrl+C(=コントロールキーを押しながらCキー)を押すとよい). _のようにLINQの式に対応したSQL文が実行されていることが分かる.

直前のSQL文の取得コマンドの実行結果
2022-01-12 15:51:19.978 JST [3704] LOG:  execute <unnamed>: SELECT t.jugyoin_no, t.buai, t.bumon_no, t.email, t.first_name, t.kanrisha_no, t.kyuyo, t.last_name, t.mei, t.phone_no, t.sei, t.shokushu_no, t.shugyo_bi
        FROM tbl_jugyoin AS t
        WHERE t.kyuyo >= 800000.0
        ORDER BY t.shugyo_bi DESC

前節と同じように,ユーザーの入力を使った絞り込みも行ってみよう. Entity Framework Core を用いる限りSQLインジェクションを気にする必要はない.LINQによる絞り込みはすべて 有効なC#の式である必要があるため,SQLインジェクションのような任意の文字列をSQL文に組み込まれる恐れは全くない. Program.cs を_に示すように変更しよう.

Program.csの内容
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
using Microsoft.EntityFrameworkCore;
using T2e;

// ① データコンテキストクラスのインスタンスの生成(データベースとの接続を開始)
var context = new SampleDbHrContext();

Console.Write("対象の従業員番号を入力: ");
int targetNo;
if (!int.TryParse(Console.ReadLine()?.Trim(), out targetNo)) Environment.Exit(1);

// ② クエリの生成(LINQを使う)
var result = (from j in context.TblJugyoins
             where j.JugyoinNo == targetNo
            select j).FirstOrDefault();

// ③ 結果の読み取り
if(result != null)
{
    Console.WriteLine($"jugyoin_no = {result.JugyoinNo}, sei = {result.Sei}, mei = {result.Mei}");
    Console.WriteLine($"     kyuyo = {result.Kyuyo,10}, shugyobi = {result.ShugyoBi:yyyy-MM-dd}");
}//if

書き換えたら実行してみよう.実行すると従業員番号の入力を求められるので適当な従業員番号(例えば100)を入力して Enterキーを押す.実行結果は_のようになる.

対象の従業員番号を入力: 100
jugyoin_no = 100, sei = 孫, mei = 正義
     kyuyo =    2400000, shugyobi = 1999-06-17
実行結果

  1. 2024年3月13日時点 ↩︎

  2. タブのタイトルは「 開発用PowerShell 」もしくは「 Developer PowerShell 」となっている. ↩︎ ↩︎ ↩︎

Last updated on 2024-04-26
Published on 2024-04-26

Powered by Hugo. Theme by TechDoc. Designed by Thingsym.