Architect's Log

I'm a Cloud Architect. I'm highly motivated to reduce toils with driving DevOps.

Entity Frameworkコードファーストを試してみた

型付きデータセット + テーブルアダプターの抱える問題

データアクセスは、型付きデータセット + テーブルアダプターにずっと頼ってました。でも採用することにためらいを感じ始めました。

複数人で同時に開発すると、コンフリクトが発生します。でも型付きデータセットは、すべてのソースとデザイナのXMLが1ファイルに生成されます。長大で複雑過ぎて、マージなんてとてもできません。

Linq To SQLでもEntity Framework(EF)でも、同じ問題が発生すると思っていたのですが、EF 4.1からコードファーストなるものが提供され、POCO(plain old CLR object。サブクラスではなく、インターフェースも実装していないプレーンなクラスのこと)で、実装できるそうなので、試してみました。

検証環境

VS2012、.NET 4.5

手順

プロジェクトを作成します。

今回はとりあえずコンソールアプリケーションで。

NuGetからEFをインストールします。
    1. メニューの[ツール] - [ライブラリパッケージマネージャー] - [ソリューションのNuGetパッケージの管理]でNuGetを起動
    2. EFをインストールします。

これだけで、ライブラリのダウンロード、参照設定、App.configの設定まで自動でやってくれます。便利になりましたね。

コードを書きます。

User.cs

POCOクラスです。これを元にテーブルが作られます。

namespace EFCodeFirstSample {
    public class User {
        public int Id { get; set; }
        public string Name { get; set; }
        public int Age { get; set; }
    }
}
MyContext.cs

DbContextを継承し、DbSetをプロパティにもつクラスを実装します。

using System.Data.Entity;
namespace EFCodeFirstSample {
    public class MyContext : DbContext {
        public DbSet<User> Users { get; set; }
    }
}
Program.cs
using System;
using System.Linq;
namespace EFCodeFirstSample {
    class Program {
        static void Main(string[] args) {
            using (MyContext context = new MyContext()) {
                // 登録
                context.Users.Add(new User() { Name = "Sato Taro", Age = 19 });
                context.Users.Add(new User() { Name = "Suzuki Jiro", Age = 20 });
                context.SaveChanges();

                // 20才以上の名前を取得
                var names = context.Users.Where(u => 20 <= u.Age).Select(u => u.Name);
                foreach (string name in names) {
                    Console.WriteLine("名前:" + name);
                }
            }
        }
    }
}
App.config

接続文字列を設定します。nameにはDbContextを継承したクラスの名前を設定します。

<connectionStrings>
    <add name="MyContext" providerName="System.Data.SqlClient" ConnectionString="Data Source=(local);Database=MyDB;UserID=sa;Password=***" />
</connectionStrings>

「コードファースト」ですから、やることはこれで全部です。デザイナ作ってません。DB作ってません。SQL書いてません。

実行する。

"名前:Suzuki Jiro"が出力されます。

実行後

作成されたDBの確認

ちゃんと、MyDBと以下のテーブルが作成され、レコードがINSERTされていました。

CREATE TABLE [dbo].[Users](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [Name] [nvarchar](max) NULL,
    [Age] [int] NOT NULL,
CONSTRAINT [PK_dbo.Users] PRIMARY KEY CLUSTERED
(
    [Id] ASC
)WITH (
    PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
    IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON
) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

Idがプライマリキーになるのは、規約です。クラスのプロパティ名が"Id"または、クラス名 + "Id"になっていると、自動的に設定されます。(プロパティ名を変えたければ、[Key]属性で、明示的にキーのプロパティを指定することもできます)

発行されたSELECT文の確認

事前にProfilerを起動し、発行されているSELECT文を確認しました。

SELECT
[Extent1].[Name] AS [Name]
FROM [dbo].[Users] AS [Extent1]
WHERE 20 <= [Extent1].[Age]

ちゃんとWHEREが指定され、必要な列だけが取得されています。

課題

NameがNull許可になってしまうのが気に入らないので、方法を調べます。