Entity Framework CoreでCRUD処理を実装
ここでは、Windows Formsアプリを作成して、Entity Framework Core(以降、EF Core)とData Servicesの連携を見ていきましょう。EF Coreを使うと、データベースファーストのアプローチで、シンプルなコードでCRUD処理を実装できます。
Windows Formsアプリを作成する
Windows Formsアプリを作成します。[新しいプロジェクトの作成]から、「Windows Formsアプリ」などを選択して、プロジェクト名には「EFCoreWinFormApp」などと適当な名前を指定してプロジェクトを作成します。
必要なパッケージは、C1.EntityFrameworkCore.CSVです。データソースの利用に必要なC1.AdoNet.CSVパッケージは依存関係で自動的にインストールされます。
[NOTE]Scaffoldingの利用
Microsoft.EntityFrameworkCore.Toolsパッケージをインストールして、Scaffoldingによりファイル群を作成する方法もあります。ただし、フィールド名がCSVの列名そのまま(マルチバイト文字)になるなどの問題点があるので、本記事では使用していません。
エンティティクラスを作成する
EF Coreにおけるデータの入れ物となるエンティティクラスを作成しておきます。このデータクラスは、CSVファイルから返されるレコードの各フィールドを保持する役割を持ちます。プロジェクトのルートフォルダにData.csファイルを以下のような内容で作成します。
using Microsoft.EntityFrameworkCore; using System.ComponentModel.DataAnnotations.Schema; namespace EFCoreWinFormsApp (1) { public partial class CustomerCSV (2) { // CSVの列名とプロパティを対応付ける [Column("No")] (3) public int Id { get; set; } (4) [Column("氏名")] public string Name { get; set; } [Column("氏名(ひらがな)")] public string NameKana { get; set; } [Column("メールアドレス")] public string Email { get; set; } [Column("電話番号")] public string Tel { get; set; } [Column("郵便番号")] public string ZipCode { get; set; } [Column("住所")] public string Address { get; set; } [Column("会社名")] public string Company { get; set; } } }
(1)の名前空間の指定は、フォームのものと同じになるように指定してください(この場合はEFCoreWinFormsApp)。(2)では、クラスCustomerCSVを定義しています。これが、EF Coreにおけるデータの入れ物になります。partialキーワードによって部分クラスとしているのは、データモデルを自動生成した場合に、独自に追加するプロパティやビジネスロジックを分離するためです。今回はあえてpartialとする意味はないのですが、EF Coreにおけるエンティティクラスの一つの構成として押さえておくとよいでしょう。
(3)では、CSVファイルにおける列名をSystem.ComponentModel.DataAnnotations.Schema.Column属性で指定しています。この属性と続く(4)のプロパティ定義において、列名とプロパティが結び付けられます。なお、EF CoreではIdプロパティは暗黙的に主キーと見なされるので、NoプロパティではなくIdプロパティとしています。
データベースコンテキストを作成する
EF Coreでデータアクセスを担当し、データベースコンテキストクラスを作成します。同じくData.csファイルのEFCoreWinFormsApp名前空間に、以下のコードを追記します。
…略… namespace EFCoreWinFormsApp { …略… public partial class CSVContext : DbContext (1) { // コンストラクタ。自動トランザクションを無効にする public CSVContext() (2) { Database.AutoTransactionBehavior = AutoTransactionBehavior.Never; } public CSVContext(DbContextOptions<CSVContext> options) (3) : base(options) { Database.AutoTransactionBehavior = AutoTransactionBehavior.Never; } // イベントハンドラからのデータベースコンテキストアクセスのためのプロパティ public virtual DbSet<CustomerCSV> CustomerCSV { get; set; } (4) // データベースコンテキストの構築時に呼び出される。CSVファイルのURIを指定 protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) (5) { if (!optionsBuilder.IsConfigured) { optionsBuilder.UseCSV("Uri='customer.csv'"); } } // モデルの作成時に呼び出される。エンティティを設定 protected override void OnModelCreating(ModelBuilder modelBuilder) (6) { modelBuilder.Entity<CustomerCSV>(entity => { entity.ToTable("customer"); }); OnModelCreatingPartial(modelBuilder); } partial void OnModelCreatingPartial(ModelBuilder modelBuilder); } }
(1)で、データベースコンテキストクラスCSVContextを定義しています。このクラスは、Microsoft.EntityFrameworkCore.DbContextクラスを継承する必要があります。こちらも、部分メソッドの利用のためにpartialキーワードを付加して部分クラスとしています。
(2)(3)はコンストラクタの定義です。オプションを渡せるオーバーロードも定義していますが、今回のサンプルでは使用していません。配下では、Database.AutoTransactionsBehaviorプロパティをNeverに指定して自動トランザクション処理を無効にしているだけです。
(4)は、データセットプロパティの定義です。このプロパティを通じて、フォームからCSVファイルにアクセスします。(5)は、OnConfiguringメソッドのオーバライドです。データベースコンテキストの構築時に呼び出されます。引数として受けとったDbContextOptionsBuilderオブジェクトのUseCSVメソッドを使って、利用するCSVファイルの接続文字列を指定しています。
(6)は、OnModelCreatingメソッドのオーバライドです。モデルの作成時に呼び出されます。引数として受け取ったModelBuilderオブジェクトを介してエンティティを設定します。ここでは、ToTableメソッドでCSVファイルに対応するテーブル名を指定しているだけですが、Propertyメソッドを使ってCSVファイルの列名とエンティティクラスのプロパティを対応付けることもできます。サンプルでは、Column属性で指定しているので、propertyメソッドの呼び出しは不要です。
フォームをレイアウトする
データが準備できたら、フォームをレイアウトします。シンプルにDataGridViewコントロールと、データ保存とレコード削除のためのButtonコントロールを配置するだけです。配置は特に問いませんが、Anchorプロパティを設定し、ウィンドウのリサイズに追従させた方が見やすくなります(図4)。また、各列の幅を自動調整するAutoSizeColumnModeプロパティをAllCellsとしています。

Windows Formsアプリのコードを記述する
イベントハンドラとして、DataGridViewコントロールへのバインドのコード、レコード削除とデータ保存のためのコードを用意します。内容はリストの通りです。
using System; using System.Windows.Forms; using Microsoft.EntityFrameworkCore; namespace EFCoreWinFormsApp { public partial class Form1 : Form { private CSVContext db = null; (1) public Form() { InitializeComponent(); } // フォームロード時にデータベースコンテキストを構築しデータソースを設定 private void Form_Load(object sender, EventArgs e) (2) { db = new CSVContext(); db.CustomerCSV.Load<CustomerCSV>(); dataGridView1.DataSource = db.CustomerCSV.Local.ToBindingList(); } // 保存ボタンクリック。変更を保存 private void SaveButton_Click(object sender, EventArgs e) (3) { db.SaveChanges(); } // フォームクローズ時にデータベースコンテキストを解放 private void Form_FormClosed(object sender, FormClosedEventArgs e) (4) { db.Dispose(); } // 削除ボタンクリック。選択行の全てに削除を実行(新規の行を除く) private void RemoveButton_Click(object sender, EventArgs e) (5) { foreach (DataGridViewRow row in dataGridView1.SelectedRows) { if (!row.IsNewRow) { dataGridView1.Rows.Remove(row); } } } } }
(1)では、データベースコンテキスト(Data.csファイルで定義したCSVContextクラス)を格納するためのprivateフィールドを準備しています。これで、フォーム中の任意のメソッドからデータベースを利用できるようになります。
(2)は、フォームの作成時に呼び出されるイベントハンドラです。(1)のprivateフィールドにインスタンスを格納し、Loadメソッドでデータセットを読み込みます。読み込んだデータソースは、ToBindingListメソッドでDataGridViewコントロールに紐づけておきましょう。
(3)は、保存ボタンクリック時に呼び出されるイベントハンドラです。データセットに変更がある場合には、SaveChangesメソッドで変更内容をCSVファイルに反映します。
(4)は、フォームの破棄時に呼び出されるイベントハンドラです。データベースコンテキストオブジェクトをDisposeメソッドによって破棄します。
(5)は、削除ボタンクリック時に呼び出されるイベントハンドラです。SelectedRowsプロパティによって選択されている行を取得し、その全ての行を、Removeメソッドで削除しています。ただし、IsNewRowプロパティで新規レコードは除外します。
以上を理解できたら、アプリを実行してみましょう。あらかじめcustomer.csvを実行ファイルのある場所(例えばbin\Debug\net9.0-windowsフォルダなど)に配置しておきます。プロジェクトを実行するとアプリが起動するので、問題なければグリッドにCSVファイルのデータが読み込まれて表示されます(図5)。

グリッドの最終行に新規レコードを追加したり、既存のレコードのデータを変更したり、行を選択して[削除]ボタンをクリックすることで、グリッドの内容が変化するのを確認してください。最後に[保存]ボタンをクリックすれば、CSVファイルに作業内容が反映されます。CSVファイルをテキストエディタなどで開いて、ファイルも変更されていることを確認してみましょう。
まとめ
今回は、CSVデータをComponentOne Data Servicesを使って操作するアプリを一括処理に対応させて、Data Servicesが大量のデータの処理にも対応できることを紹介しました。また、Entity Framework連携によるモデル指向のデータ操作を、WinFormsアプリを作成する事例を通じても紹介しました。