Entity Framework の開発スタイルの1つであるコードファーストを使用して、データベースやテーブルを作成し、データの読み書きをしてみた。
目次
(1) 動作環境
Windows Server 2016 x64
Visual Studio Enterprise 2017 Version 15.7.3
PostgreSQL 10.3
(2) プロジェクトの作成
今回は、コンソールアプリを作成する。
(3) 関連パッケージをインストール
パッケージは、NuGet を使用してインストールする。
「ソリューション エクスプローラー」の「参照」を右クリック、「NuGet パッケージの管理」をクリックする。
「参照」タブをクリックし、パッケージ名を入力して検索する。
一覧から以下のパッケージを選択して、画面右のインストールボタンをクリックすると、インストールが開始される。
- EntityFramework v6.2.0
- EntityFramework.ja v6.2.0
- EntityFramework6.Npgsql v3.1.1
- Npgsql v4.0.1
すべてのパッケージをインストールしたら、更新プログラムを確認する。
最新の安定バージョンに更新することをお勧めする。
(4) Entity (POCO) クラスの作成
プロパティとテーブルを関連付けるために、POCOクラスを作成し、アノテーションを定義する。
アノテーションについては、以下のサイトが詳しい。
- 「[entity framework]DBへのmigrationやアノテーションに関するまとめ」
https://qiita.com/az22223/items/0e8385644bd8114f4051 - 「第2回 EF 4.1の規約とデータベースの初期化方法」
http://www.atmarkit.co.jp/fdotnet/ef4basic/ef4codefirst02/ef4codefirst02_01.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace TestProject { [Table("testtest")] public class TestEntity // public にする。 { [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] [Column("id")] public int Id { get; set; } [Column("name")] [StringLength(50)] public string Name { get; set; } [Column("age")] public int Age { get; set; } } } |
他にもリレーションシップと多重度に関しては以下が詳しい。
「第2回 EF 4.1の規約とデータベースの初期化方法」
http://www.atmarkit.co.jp/fdotnet/ef4basic/ef4codefirst02/ef4codefirst02_02.html
(5) Model クラス作成
Entity を管理するための Model クラスを作成する。
「ソリューション エクスプローラー」から「プロジェクト」を右クリックし、「追加」-「新しい項目」をクリックし、起動した「新しい項目の追加」から「ADO.NET Entity Data Model」を選択する。クラスの「名前」(今回は TestModel.cs)を入力し、「追加」ボタンをクリックする。
今回は、ソースコードを元にデータベースやテーブルを作成していくので「空の Code First モデル」を選択する。
- 「データベースから EF Designer」
- 既にあるデータベースを基に ER Designer でモデル作成
- クラスはモデルから生成
- データベース接続、モデルの設定、モデルに含めるデータベース オブジェクトを選択できる
- 「空の EF DEsigner モデル」
- ER Disigner で空のモデル作成
- モデルからデータベースを作成可能
- クラスはモデルから生成
- 「空の Code First モデル」
- コードから空のモデル作成
- モデルからデータベースを作成可能
- 「データベースから Code First」
- 既にあるデータベースを基にモデル作成
- データベース接続、モデルの設定、モデルに含めるデータベース オブジェクトを選択できる
DbContext(System.Data.Entity名前空間)は、データベース接続やエンティティの管理などを担当するクラス
DbSet(System.Data.Entity名前空間)ではエンティティの集合を扱う。登録/更新/削除といったエンティティで行うべき処理をDbSetクラスで把握することができる。
モデルを作成すると、以下のソースコードが出力される。
上記ソースコードから MyEntity クラスを削除し、// public virtual DbSet のコメントを外し、以下のようになるよう編集する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
namespace TestProject { using System.Data.Entity; public class TestModel : DbContext { // コンテキストは、アプリケーションの構成ファイル (App.config または Web.config) から 'TestModel' // 接続文字列を使用するように構成されています。既定では、この接続文字列は LocalDb インスタンス上 // の 'TestProject.TestModel' データベースを対象としています。 // // 別のデータベースとデータベース プロバイダーまたはそのいずれかを対象とする場合は、 // アプリケーション構成ファイルで 'TestModel' 接続文字列を変更してください。 public TestModel() : base("name=TestModel.cs") { } protected override void OnModelCreating(DbModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); modelBuilder.HasDefaultSchema("public"); } // モデルに含めるエンティティ型ごとに DbSet を追加します。Code First モデルの構成および使用の // 詳細については、http://go.microsoft.com/fwlink/?LinkId=390109 を参照してください。 public virtual DbSet<TestEntity> TestEntities { get; set; } } } |
(6) データベース依存内容の外出し
データベースに依存する内容は、アプリケーション構成ファイル(Web.config または App.config)に記述する。これにより、データベースの設定内容が変更されても、アプリケーションを更新する必要がなくなり、汎用的になる。
(6-1) プロバイダファクトリーの設定
1 2 3 4 5 |
<system.data> <DbProviderFactories> <add name="Npgsql Data Provider" invariant="Npgsql" support="FF" description=".Net Data Provider for PostgreSQL" type="Npgsql.NpgsqlFactory, Npgsql" /> </DbProviderFactories> </system.data> |
(6-2) 接続用文字列の設定
<connectionString>直下に以下を追加。
name には、Model 名を。<connectionString>の Database には、接続先のDB名を。初回アクセス時に無ければ自動的に作成される。
1 |
<add name="TestModel.cs" connectionString="Persist Security Info=True;Username=testuser;Password=testpass;Database=testdb;Port=5432;Host=localhost" providerName="Npgsql" /> |
(6-3) <configSections> の設定
<configSections>直下に以下を追加。
1 |
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" /> |
※各パラメーターは本家ドキュメントを参照。
http://www.npgsql.org/doc/
※注意点は自動でapp.config(もしくはweb.config)に設定される以下の部分の「oldVersion」と「newVersion」をNugetでインストールされたNpgsqlのバージョンに合わせること
https://qiita.com/fujimu/items/f46430cce8c89e65e548
今回は以下のように設定した。
(7) データベースの自動生成
コード ファーストのデフォルトの挙動は、起動時にデータベースが存在しない場合、プログラム実行時に自動的に作成する。起動時にデータベースとテーブルが存在する場合、定義内容に差異があればエラーを投げる。
既にあるテーブルを消して起動すれば、新たな構成でテーブルを作り直してくれる。既にあるテーブルのデータを引き継ぎたい場合は、データベース マイグレーションを実施すれば良い。
本稿では、データベース マイグレーションを使用しないため、詳細は割愛する。
初回起動時の挙動を制御したい場合は、IDatabaseInitializerインターフェイスを実装したクラスを使用または継承したクラスを使用する。
- CreateDatabaseIfNotExists
データベースが存在しない場合にのみ自動生成を行う - DropCreateDatabaseAlways
常にデータベースを再生成する - DropCreateDatabaseIfModelChanges
データベースが存在しない場合およびモデルが変更された場合に、自動的にデータベースを削除して再生成する
詳細は以下のサイトを参考にするとよい。
「第2回 EF 4.1の規約とデータベースの初期化方法」
http://www.atmarkit.co.jp/fdotnet/ef4basic/ef4codefirst02/ef4codefirst02_03.html
(8) データ書き込み・データ読み込み
以下を実行すると自動的にデータベースとテーブルが作成され、データも挿入していることが分かる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
using System; using System.Linq; namespace TestProject { class Program { static void Main(string[] args) { Write(); Read(); } static void Write() { using (var model = new TestModel()) { model.TestEntities.Add(new TestEntity { Name = "Taro", Age = 30 }); model.TestEntities.Add(new TestEntity { Name = "Jiro", Age = 27 }); model.SaveChanges(); } } static void Read() { using (var model = new TestModel()) { var query = from test in model.TestEntities orderby test.Id select test; foreach (var item in query) { Console.WriteLine(item.Name + ", " + item.Age); } Console.WriteLine("Press any key to exit..."); Console.ReadKey(); } } } } |
(9) 開発時に発生したエラー
- [Entity Framework] System.InvalidOperationException: ADO.NET プロバイダーに、不変名が Npgsql の Entity Framework プロバイダーがありません。
- [Entity Framework] System.TypeInitializationException: System.Data.Entity.Internal.AppConfig のタイプ初期化子が例外をスローしました。
- [Entity Framework] System.Data.Entity.Core.ProviderIncompatibleException: データベースへのアクセス中にエラーが発生しました。
- [Entity Framework] PostgresException: 42P01: relation “dbo.HogeTable” does not exist
- [Entity Framework] EntitySet ‘HogeEntities’ はキーが定義されていない型 ‘HogeEntity’ に基づいています。
以上。