[PostgreSQL版] Entity Frameworkで世界が変わったでござる

[PostgreSQL版] Entity Frameworkで世界が変わったでござる

どうもこんばんは!HYPのこうじです。

今まで自分で使いやすい様にライブラリ化したりと色々試行錯誤していたSql管理ですが、最近Entity Entity Frameworkに出会って人生変わりました!
ので、Visual Studio Community 2015を利用したEntity Frameworkの導入をご紹介したいと思います!

といっても僕自身詳しい訳ではなく、自分が必要な範囲で楽しく使っているだけなので、専門的な突っ込んだ話はしません。
さらに言うと、MySQLの方ではなくてPostgreSQLの方です。

Entity Frameworkを使おう!

NuGetで簡単インストール

Visual Studioを利用すれば、NuGetを使って簡単にインストールが可能です。

NuGET パッケージ管理を選択

ソリューションエクスプローラーから使用するプロジェクトの参照でNuGetパッケージ管理を開きます。

entity-framework-install-1

パッケージをインストール

EntityFramework6.Npgsql』で検索しましょう。
出てきたパッケージをインストールします。

インストール開始

entity-framework-install-2

インストール完了

entity-framework-install-3

インストールパッケージの確認

インストールが完了したら、3つのライブラリが『インストール済み』に表示されます。

entity-framework-install-4

App.configに追加。

以下の内容のApp.configの最後に追加します。

entity-framework-install-6

entity-framework-install-7

<system.data>
  <DbProviderFactories>
    <add name="Npgsql Data Provider" invariant="Npgsql" support="FF" description=".Net Framework Data Provider for Postgresql" type="Npgsql.NpgsqlFactory, Npgsql" />
  </DbProviderFactories>
</system.data>

おわり

あれ。これだけでした。めちゃくちゃ簡単ですね!!

どちらかと言うとインストールの仕方よりも、Entity Frameworkでデータベースのやり取りがめちゃくちゃラクになった!と言うのを伝えたかったんです!

使い方

それでは一般例です。

Table1.cs

データベースのテーブルを再現したデータクラスです。

using System;
using System.ComponentModel.DataAnnotations;        // 参照を追加。
using System.ComponentModel.DataAnnotations.Schema; // 参照を追加。

namespace ConsoleApplication1
{
    [Table("table_info")] // テーブルの名前を入力。
    // [Table("table_info", schema = "name")] // スキーマをここで設定する事も可能。
    public class TableInfo
    {
        [Key] // 主キーを設定。
        [Column("id")] // データベース上のカラム名を入力。
        public int Id { get; set; }

        [Column("name")]
        public string Name { get; set; }

        [Column("address")]
        public string Address { get; set; }

        [Column("last_sign")]
        public DateTime LastSign { get; set; }
    }
}

TestConnection.cs

データベースと接続するためのクラス。

using System.Text;
using System.Data.Entity;   // 参照を追加。
using Npgsql;               // 参照を追加。

namespace ConsoleApplication1
{
    public class TestConnection : DbContext // DbContextを継承。
    {
        #region 接続プロパティ

        /// <summary>
        /// テーブル接続情報。
        /// </summary>
        public DbSet<Table1> Table { get; set; }

        /// <summary>
        /// 参照するスキーマ。
        /// </summary>
        public string DefaultSchema { get; private set; }

        #endregion

        #region イベント

        /// <summary>
        /// スキーマを変更したい場合はここで変更。
        /// 指定が無いとスキーマ名は『dbo』に設定される。
        /// </summary>
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
            => modelBuilder.HasDefaultSchema(DefaultSchema);

        #endregion

        #region メソッド

        /// <summary>
        /// コネクションの取得。
        /// </summary>
        /// <param name="userid">ユーザーID</param>
        /// <param name="password">パスワード</param>
        static NpgsqlConnection GetConnecting(string userid, string password)
        {
            var sb = new StringBuilder();
            sb.Append($"Server=接続先ホスト;")
            .Append($"Port=接続ポート番号;")
            .Append($"Database=データベース名;")
            .Append($"User Id={userid};")
            .Append($"Password={password};");
            return new NpgsqlConnection(sb.ToString());
        }

        #endregion

        #region コンストラクタ。

        /// <summary>
        /// コンストラクタ。
        /// </summary>
        /// <param name="userid">接続ID。</param>
        /// <param name="password">接続パスワード。</param>
        /// <param name="defaultSchema">接続先スキーマ。</param>
        public TestConnection(string userid, string password, string defaultSchema)
            : base(GetConnecting(userid, password), true)
        {
            DefaultSchema = defaultSchema;
        }

        #endregion

    }
}

program.cs

実際にEntity Frameworkで処理するコードです。

using System;
using System.Linq;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            // 接続インスタンスを作成。
            var dbc = new TestConnection("ユーザーID", "パスワード", "接続スキーマ");

            //
            // データベースの内容を取得(SELECT文)。
            //
            {
                // 全ての情報を取得。
                var allData = dbc.Table.ToList();

                // 絞り込んで取得。
                var filterData = dbc.Table.Where(w => w.Name == "絞り込むID").ToList();
            }
            //
            // 内容を変更(UPDATE文)。
            //
            {
                // 変更したいレコードを取得。
                var data = dbc.Table.First(f => f.Id == 0);

                // 取得データの内容を変更。
                data.Name = "変更する名称";
                data.LastSign = DateTime.Now;

                // 変更を反映。
                dbc.SaveChanges();
            }
            //
            // 新しいデータを登録(INSERT文)。
            //
            {
                // 登録する新規データの入れ物を作成。
                var data = dbc.Table.Create();

                // 内容を設定。
                data.Id = 1; // serial型の場合は無くても勝手に登録される(ハズ)。
                data.Name = "林 航嗣";
                data.Address = "kouji@creatorhyp.com";
                data.LastSign = DateTime.Now;

                // レコードををテーブルに登録。
                dbc.Table.Add(data);

                // 設定を反映。
                dbc.SaveChanges();
            }
            //
            // データを削除(DELETE文)。
            //
            {
                // 削除したいレコードを取得。
                var data = dbc.Table.First(f => f.Id == 1);

                // レコードを削除。
                dbc.Table.Remove(data);

                // 変更を反映。
                dbc.SaveChanges();
            }
        }
    }
}

なぜEntity Frameworkが便利なのか

長々としたSQL文を打つ必要が無くなりました。

もうこれに尽きると思います。

まじで。

泣きそうです。

涙出てきた。

例えばSELECT文にしてもめちゃくちゃ簡素に解決出来ます。

//
// Entity Frameworkの場合。
//
{
    // データを出力。
    dbc.Table.ToList().ForEach(f =>
    {
        Console.WriteLine($"{nameof(f.Id)} : {f.Id}");
        Console.WriteLine($"{nameof(f.Name)} : {f.Name}");
        Console.WriteLine($"{nameof(f.Address)} : {f.Address}");
        Console.WriteLine($"{nameof(f.LastSign)} : {f.LastSign}");
    });
}
//
// 通常の場合。
//
{
    // サーバー接続文。
    var connString = "Server=接続ホスト;Port=ポート番号;User Id=ユーザーID;Password=パスワード;Database=データベース名;";

    // データベースに接続開始。
    using (var conn = new NpgsqlConnection(connString))
    {
        // 接続。
        conn.Open();

        // SQLコマンドを作成。
        var command = new NpgsqlCommand("SELECT * FROM table_info;", conn);

        // データを取得。
        var dataReader = command.ExecuteReader();

        // 全てのデータを出力。
        while (dataReader.Read())
        {
            Console.WriteLine($"Id : {dataReader["id"]}");
            Console.WriteLine($"Name : {dataReader["name"]}");
            Console.WriteLine($"Address : {dataReader["address"]}");
            Console.WriteLine($"LastSign : {dataReader["last_sign"]}");
        }
    }
}

こんな感じでEntity Frameworkの場合は、見た目もスッキリ管理出来ますね!

まず何が便利かと言うと、型の定義が通常使うようなクラスで定義する事が出来る
これがめちゃくちゃ便利。

DataReaderだとどこかしらに、余分にテーブル用のデータクラスを作っておいて、それを参照しない限り上記のような直打ちになるところを、Entity Frameworkだとあらかじめテーブル接続用のEntityをクラスで作成してしまうので、普通にオブジェクトを扱う感覚でデータベースと接続出来ちゃいます。泣きそう。

更新時も必要な更新のみを反映。

これも昔は自分でライブラリ作ってごにょごにょしてましたが、変更を掛けた部分のみ更新を掛けてくれます。

// 変更したいレコードを取得。
var data = dbc.Table.First(f => f.Id == 0);

// Nameのみ変更。
data.Name = "変更する名称";

// 変更を反映。
dbc.SaveChanges();

//
// デバッグ出力してみると、Nameのみ更新処理をしているのが分かります。
//
UPDATE "schema_name"."table_info" SET "name"=@p_0 WHERE "id" = @p_1
-- p_0: '変更する名称' (Type = Object, IsNullable = false)
-- p_1: '0' (Type = Int32, IsNullable = false)
-- Executing at 2016/06/05 23:40:07 +09:00
-- Completed in 24 ms with result: 1

さようならSQL文

こんな感じで普通にプログラミングするイメージでデータベースのやり取りが出来てしまいます。泣きそうです。

今までは、メイン言語に加えてSQL文を組み合わせてやらなくてはいけなかったところを、『SQLのやり取りは全てEntity Framework内に内包され、一つの言語で完結出来る』というのは非常に管理もしやすくありがたいモノですね!

こういう風に、ソースコードのみで設計が行える形式をコードファーストと言うらしいです。

使用前のエラーまとめ

導入時に、詰まったエラーがいくつかあったので参考までに載せときますね。

ライブラリが競合しています。

みたいなエラーがでて、エラーになってしまいました。
調べたところ、同じ参照ライブラリにバージョン違いがあると出るエラーみたいですが、そもそも他で使ってなくても出てしまう状態です。

僕の場合はNuGetパッケージの更新プログラムを最新に更新する事で解決しました。

entity-framework-install-5

新規のレコード登録が出来ない。

これで正直半日潰したんですが(笑)、どうやらプロパティ名に『ID』と言う文字が入る場合は、数値型のIDENTITY型(Postgresはserial型)として扱われる様です。

その場合は、プロパティの属性に『DatabaseGenerated(DatabaseGeneratedOption.None)』を入れると、通常の型として使用出来ました。

[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)] // 編集が出来る型に変更。
[Column("id")]
public int Id { get; set; }

配布時にデータベースに接続できない。

これはごもっともな話ですが、構成ファイル自体を知らなかった僕は、ビルド時に出てくる実行ファイルのconfigファイル(.exe.config)を入れるのを忘れてました(笑)。アホですね。

entity-framework-install-8

おわり

今までは、

  1. SQL接続関連をまとめた自前ライブラリで対応。
  2. ADO.NETを知って、DataSetやDataTableを利用したデータベース間のやり取り。

という流れでしたが、どれもかならずSQL文を利用した更新や削除等の冗長になりがちなソース管理が気に入りませんでした。
まぁ常に全カラム更新!常に全カラム登録!とかならそりゃ楽ですけどね。あまりよろしくないですよね。

『何故今さらEntity Framework?』と思うかもしれませんが、当時はLinqもラムダ式も知らないにわかでしたので、見ても『あ、これはC#じゃ使えんな』という謎結論を展開していました(笑)。

そんなこんなで最近ようやく重い腰を上げて改めて使ってみたら『あ、空って青かったんや』と思えるレベルにラクになりました。

と言う訳で、みなさんもEntity Frameworkで、つらつらと書いていたSQL文から卒業しましょう!(笑)

参考サイト

以下のサイトにお世話になりました!

第1回 EF 4.1の目玉機能「コード・ファースト」を理解しよう

.NETライブラリ「Npgsql」によるPostgreSQLの活用

【C#】PostgreSQLでEntityFrameworkの使用方法#2

実践 Entity Framework ~ テーブルとフィールド名を変更する

記事一覧

HYPについて