lycheejam's tech log

チラ裏のメモ帳 | プログラミングは苦手、インフラが得意なつもり。

C# EntityFrameworkを使ったDBアクセス コードファースト編

2018/05/23 追記

本記事の内容を書き直しました。
なるべく適当な言葉は減らしてるつもりです。

kitigai.hatenablog.com

概要

C# EntityFrameworkのコードファストを利用してDBアクセスを行います。

先日書いたASP.NET MVCを使ったTweetを投稿するWebアプリケーションの制作過程で
DBアクセスについてよくわからなかったので
チュートリアルをさらさらっとやった次第ですので備忘録として残しておきます。

EntityFrameworkとは何ぞや?

DBとのやり取りはDBMSと言われるDB管理システムを使うのはご存知の通り
これ以外知りません

そのDBMSとのやり取りをC#で行うためのフレームワークがEntityFramework
これ以上の理解はこちらへどうぞ

C#でDBアクセスが簡単にできる便利機能くらいの理解で良いのではないでしょうか?
(これくらいの理解にとどめておきたい)

Code First / DatabaseFirst

正確にはCode First / Database First / Model Firstの3種類
(これ以外にもあるかもしれない)

これらはEntityFrameworkを利用した開発手法の呼び方です。
わかりやすく解説していこちらの記事が大変参考になりました。

Code FirstはC#のコードでDBを自動生成してC#のコードを中心に
データアクセスのプログラムを作成することが可能になる

もっと言えばC#のコードでクラス(Entityクラス)を定義して
そこからデータベースを自動生成する。

EntityFrameworkのCode FirstでCRUD

環境

ソース

github.com

プロジェクトの作成

新規作成 → プロジェクト → コンソールアプリケーション

EntityFrameworkのインストール

NuGetからEntityFrameworkをインストール
インストール開始後、以下のようなライセンス同意画面が出るので「同意」を押下

f:id:HM_Atlas:20180110214104p:plainf:id:HM_Atlas:20180110214105p:plain

インストール完了後、プロジェクトの参照にEntityFrameworkが追加されていることを確認
f:id:HM_Atlas:20180110214106p:plain

Entityクラスの作成

データベースに格納されるオブジェクトの定義をする。
プロジェクト直下に「Models」フォルダを追加

Book(書籍)クラスとAuthor(著者)クラスを作成

  • Book(書籍)クラス
namespace EntityFrameworkTest.Models {
    public class Book {
        //IDは自動生成されるはず
        public int Id { get; set; }
        public string Title { get; set; }
        public int PublishedYear { get; set; }
        public virtual Author Author { get; set; }
    }
}
  • Author(著者)クラス
namespace EntityFrameworkTest.Models {
    public class Author {
        //Bookと同じくIDは自動生成されるはず
        public int Id { get; set; }
        public string Name { get; set; }
        public DateTime Birthday { get; set; }
        public string Gender { get; set; }
        public virtual ICollection<Book> Books { get; set; }
    }
}

[メモ]virtualとは

BookクラスのプロパティとしてAuthor型を作成したが
Entityクラス内で”他の”Entityクラスをプロパティとして定義するには
Virtualを指定する必要がある。
また、Authorクラスでコレクションとして定義しているのも先ほどと同様

DbContextクラスの作成

「Models」フォルダを右クリック → 追加 → 新しい項目
ADO.NET Entity Data Model」を選択、任意の名前で追加
(任意の名前とは言ってもDbContextの部分はいじってもいいものなのか不明、たぶんダメ?)

f:id:HM_Atlas:20180110214107p:plainf:id:HM_Atlas:20180110214108p:plain

追加ボタン押下後、ウィザードが出るので「空のCode Firstモデル」を選択
f:id:HM_Atlas:20180110214109p:plain

完了後、「Models」フォルダにBooksDbContext.csファイルが追加されていることを確認
DbContextクラスを定義する。(下のソース、自動生成部分はしょってます。)

    public class BooksDbContext : DbContext {
        public BooksDbContext() : base("name=BooksDbContext") { }

        public DbSet<Book> Books { get; set; }
        public DbSet<Author> Authors { get; set; }
    }

BooksDbContext.csの中にはDbContextクラスを継承(?)した
BooksDbContextクラスが定義されている。

さらにDbSet<T>でBooksとAuthorsと言うプロパティを定義
BooksがBookオブジェクトを格納するテーブル名になります(Authorsも同じく)

DB接続文字列の確認

何も触ってないのでいじる必要はありませんが確認だけ
プロジェクト直下/App.config
<connectionStrings>で囲われている部分

実行

Mainのコードは何も書いていないがとりあえず実行
初回はDBの作成等でいつものコンソールアプリケーションの
起動よりは時間がかかる気がするがすぐに完了する。

Visual Studioのメニュー[表示]からSQL Server オブジェクトエクスプローラーを選択
以下の画像のようにDBとテーブルが作成されていることを確認する。
f:id:HM_Atlas:20180110214111p:plain

[メモ]__MigrationHistory

EntityFrameworkが自動生成
//詳しくは調べる

Create

テーブルに追加するデータの用意
InsertBooksメソッドを作成

        /// <summary>
        /// データ追加
        /// </summary>
        static void InsertBooks() {
            //usingを使用しているのでブロックを抜けた時に
            //BooksDbContextオブジェクトは破棄される
            using (var db = new BooksDbContext()) {
                //書籍データ生成
                var book1 = new Book {
                    //IDは自動生成の為、登録しない
                    Title = "マスカレード・ホテル",
                    PublishedYear = 2011,
                    Author = new Author {
                        Birthday = new DateTime(1958, 2, 4),
                        Gender = "M",
                        Name = "東野圭吾"
                    }
                };
                //book1の追加
                db.Books.Add(book1);
                //書籍データ生成
                var book2 = new Book {
                    Title = "沈まぬ太陽",
                    //おそらく文庫本の発行年
                    PublishedYear = 2001,
                    Author = new Author {
                        Birthday = new DateTime(1924, 1, 2),
                        Gender = "F",
                        Name = "山崎豊子"
                    }
                };
                //book2の追加
                db.Books.Add(book2);
                //DBの更新
                db.SaveChanges();
            }
        }

書籍情報だけでなくAuthorプロパティで著者情報も同時に登録

BooksDbContextオブジェクトをdbとして生成
以後このdbを使ってDBアクセスを行います。(CRUD共通)
この作成したDBアクセスのオブジェクトは破棄されるまで有効

実行

テーブルの確認
f:id:HM_Atlas:20180110214112p:plain

Read

Readメソッドを実行
f:id:HM_Atlas:20180110214113p:plain

Update

Updateメソッドを実行
※ここから画像がございませんが昨日の深夜
コードを書いていた自分はちから尽きたのでしょう・・・

Delete

Deleteメソッドを実行

クエリ

//すんません飛ばしました。

[メモ]Entity FrameworkでもLINQが使える

LINQ to Entity」と言うらしい
LINQ to Objectとほぼ同じような使い方ができるらしい

LINQの記述がSQLに翻訳されて実行されるため
SQL文を書くことなくDBを操作できる。

アノテーションと自動マイグレーション

アノテーション

アノテーション(データ注釈ともいうらしい)は
DBのテーブルのカラム(項目)に制約を付けることができる。
(個人的には型の指定ができるの方がしっくりくる)
アノテーションに関しては参考文献がいっぱいあったので調べれば何とかなりそうなノリ
とりあえずアノテーションを付ける
以下を参照

System.ComponentModel.DataAnnotations

Bookクラスにアノテーションを付ける

    public class Book {
        public int Id { get; set; }
        [Required]
        public string Title { get; set; }
        public int PublishedYear { get; set; }
        public virtual Author Author { get; set; }
    }

[Required]がアノテーション
Required属性は指定したプロパティが必須項目であることを
Entity Frameworkに指示するらしい

SQLでのテーブル定義で言えば「NOT NULL」ですよね

自動マイグレーション

自動マイグレーションC#のコードを変更することで
自動でデータベースのテーブル定義を変更してくれる機能
(本にはデータベース構造を自動で変更とあった)

先ほどのBookクラスのプロパティの型を変更してみる

    public class Book {
        public int Id { get; set; }
        [MaxLength(30)]
        [Required]
        public string Title { get; set; }
        public int PublishedYear { get; set; }
        public virtual Author Author { get; set; }
    }

[MaxLength(30)]を追加して最大文字数を30字に設定

実行

f:id:HM_Atlas:20180110214115p:plain

oh...

自動マイグレーションの機能は既定で無効となっているのでこのエラーが出ました
なので有効化します。

自動マイグレーション機能の有効化

以下を追加

Models/Configuration.cs

namespace EntityFrameworkTest.Models {
    internal sealed class Configuration : DbMigrationsConfiguration<BooksDbContext> {
        //自動マイグレーション機能を有効にする。
        public Configuration() {
            AutomaticMigrationsEnabled = true;
            AutomaticMigrationDataLossAllowed = true;
            ContextKey = "EntityFrameworkTest.Models.BooksDbContext";
        }
    }
}

これはわかりやすくていいですね
単純に機能を有効化しているので

BooksDbContextクラスにSetInitializerメソッドを追加

Models/BooksDbContext.cs

    public class BooksDbContext : DbContext {
        public BooksDbContext()
            : base("name=BooksDbContext") {
            //自動マイグレーション機能を有効にするための何かだけど
            //何をやっているのかはわからない
            Database.SetInitializer(new MigrateDatabaseToLatestVersion<BooksDbContext, Configuration>());
        }
       //...省略
    }

コメントの通りです。後で調べます。

実行

テーブルの項目型が変更されていますね。

f:id:HM_Atlas:20180111000448p:plainf:id:HM_Atlas:20180111000424p:plain
初めてリサイズしてみたんですけど結局サイズかみ合わず

メモ

  • 主キーの指定

Bookクラスで言うとIDプロパティ
IDENTITY属性で設定される。(自動インクリメントのINDEX番号)
アノテーションで指定してやればいいのだけれど
自動で設定される場合、判定の基準はなんなんでしょうか
//調べる

参考文献

雑感

こんなにスクショ取るんじゃなかった
そしてブログに貼り付ける大きさではない