Entity Framework CTP 5 Code First ウォークスルー


「Code Firstは気持ちいい」 、私の前のデスクに座っている @haruulala さんが呟いていました。

確かに、コーディング大好きデベロッパーには納得の一言だと思います。今日は、2010年では最後のバージョンとなる CTP5 を実際に動かしてみましょう。ちなみにCTP4からは、一部変更されているところもあります。変更点については以前のポスト(Entity Framework Code First CTP5 リリース)を参考にしてください。  「そもそもCode Firstって何よ?」という方も以前のポスト(ADO.NET Entity Framework Code First を使ってみよう)を参考にしてください。

 

必要な環境

・Visual Studio 2010

・SQL Server 2008/R2 Express 

 

1.EF CTP5をインストール

※以前のバージョンが入っていても削除する必要はありません。

 

2.コンソールアプリ作成

    1. Visual Studio 2010 を起動
    2. ファイル-> 新規作成 -> プロジェクト
    3. プロジェクト名は”CodeFirstSample”としてコンソールアプリケーション作成
    4. “OK” をクリック

 

3.モデル実装

検証用であればProgram.csにそのままつくっちゃいましょう。

ここでは1:Nの関係をもつエンティティを2つ定義しています。

(本当は多:多の方がEFのメリットが分かりやすいかもしれません・・・今回はシンプルさを重視して1:N)

public class Category
{
    public string CategoryId { get; set; }
    public string Name { get; set; }

    public virtual ICollection<Product> Products { get; set; }
}

public class Product
{
    public int ProductId { get; set; }
    public string Name { get; set; }
    public string CategoryId { get; set; }

    public virtual Category Category { get; set; }
} 

  

4.コンテキスト 実装

コンテキストの作成には以下2つのオブジェクト(System.Data.Entity.DbContext、System.Data.Entity.DbSet<TEntity>)が必要になります。そのため、以下二つのコンポーネントを参照追加します。

    • EntityFramework
    • System.Data.Entity

これも検証用であればProgram.csにそのままつくっちゃいましょう。using System.Data.Entityをお忘れなく。

・・
using System.Data.Entity;
public class ProductContext : DbContext
{
    public DbSet<Category> Categories { get; set; }
    public DbSet<Product> Products { get; set; }
}

以上で完成です。コードファーストでは”Create Database”や”Create Table”やデザイナでモデルを作るなどなど、様々な作業を省いて実装を簡潔にします。

 

5.実行 1

Program.csに次のコードを追加して、実行してみましょう。

class Program
{
    static void Main(string[] args)
    {
        using (var db = new ProductContext())
        {
            // カテゴリの追加 
            var food = new Category { CategoryId = "FOOD", Name = "Foods" };
            db.Categories.Add(food);
            int recordsAffected = db.SaveChanges();

            Console.WriteLine(
                "Saved {0} entities to the database, press any key to exit.",
                recordsAffected);

            Console.ReadKey();
        }
    }
} 

SQL Server Management Studio を使って、SQLExpressの中身を見てください。

”CodeFirstSample.ProductContext”という名前でDBが作成されています。

また、テーブル(Categories)が作成されています。

この辺りのデフォルト値は規約で決まっていまるわけですが、カスタマイズ可能です。これについても後程ふれます。

 

6.実行2

他の処理も確認してみましょう。

EFやLINQをご存じの方であれば、そのままのスキルで実装可能です。

class Program
{
    static void Main(string[] args)
    {
        using (var db = new ProductContext())
        {
            // カテゴリの存在チェック
            var food = db.Categories.Find("FOOD");
            if (food == null)
            {
                food = new Category { CategoryId = "FOOD", Name = "Foods" };
                db.Categories.Add(food);
            }

            // プロダクトの追加 
            Console.Write("Please enter a name for a new food: ");
            var productName = Console.ReadLine();

            var product = new Product { Name = productName, Category = food };
            db.Products.Add(product);

            int recordsAffected = db.SaveChanges();

            Console.WriteLine(
                "Saved {0} entities to the database.",
                recordsAffected);

            // プロダクトを取得
            var allFoods = from p in db.Products
                            where p.CategoryId == "FOOD"
                            orderby p.Name
                            select p;

            Console.WriteLine("All foods in database:");
            foreach (var item in allFoods)
            {
                Console.WriteLine(" - {0}", item.Name);
            }

            Console.WriteLine("Press any key to exit.");
            Console.ReadKey();
        }
    }
} 

 

補足ですがLINQを利用した場合、従来のEFと同様に遅延ロードや、Includeオプションの指定も可能です。

遅延ロードの例

using (var db = new ProductContext()) {

     var allcats = from c in db.Categories

                   select c;

     foreach (var c in allcats)

     {

         Console.WriteLine(c.Name);

         foreach (var p in c.Products)

         {

             Console.WriteLine(p.Name);

         }

     }

}

 

Includeオプションの例

using (var db = new ProductContext()) {

     var allcats = from c in db.Categories.Include("Products")

                   select c;

     foreach (var c in allcats)

     {

         Console.WriteLine(c.Name);

         foreach (var p in c.Products)

         {

             Console.WriteLine(p.Name);

         }

     }

}

 

7.DBの名前を変更

”CodeFirstSample.ProductContext”という名前のDBを”MyProductDatabase”という名前に変更してみましょう。

作成したContextの実装を以下のように修正します。するとbaseに指定した名称にDB名が変更されます。

public class ProductContext : DbContext
{
    public ProductContext()
        : base("MyProductDatabase")
    { }

    public DbSet<Category> Categories { get; set; }
    public DbSet<Product> Products { get; set; }
} 

補足ですが接続文字列を使っても、DB名、インスタンス名、認証方式など指定できます。

例えば設定ファイル(App.config)に下記の設定を追加すると、MSSQLServerにProductsデータベースが生成されます。

<configuration>

  <connectionStrings>

    <add name="MyProductDatabase"

              connectionString="Data Source=.;Initial Catalog=Products; Integrated

              Security=True;MultipleActiveResultSets=True"

              providerName="System.Data.SqlClient"/>

  </connectionStrings>

</configuration>

 

8.スキーマ変更

スキーマの変更も可能です。

例えば、エンティティの追加です。

以下のSupplierをProgram.csへ追加しましょう。

public class Supplier
{
    public string SupplierCode { get; set; }
    public string Name { get; set; }
}

Contextも次のようにSupplierに関するメソッドを追加します。

public class ProductContext : DbContext
{
    public ProductContext()
        : base("MyProductDatabase")
    { }

    public DbSet<Category> Categories { get; set; }
    public DbSet<Product> Products { get; set; }
    public DbSet<Supplier> Suppliers { get; set; }
}

この状態で実行すると、Supplierがキーを持っていないというエラーが発生してしまいます。

「あれっ??なんだよ、勝手にSupplierCodeをキーにしてくれよ~。」と愚痴りたくなるかもしれませんが、SupplierCodeがプライマリーキーの規約にマッチしないのが原因です。詳しくはこちらを参照してください。

従って、明示的にキーを指定する必要があります。

参照の追加で”System.ComponentModel.DataAnnotations”コンポーネントを追加して、以下の名前空間を追加してください。

using System.ComponentModel.DataAnnotations;

そして、次のようにKey属性を追加します。

public class Supplier
{
    [Key]
    public string SupplierCode { get; set; }
    public string Name { get; set; }
}

ちなみにCTP5でサポートしているアノテーションは以下の通り。

  • KeyAttribute
  • StringLengthAttribute
  • MaxLengthAttribute
  • ConcurrencyCheckAttribute
  • RequiredAttribute
  • TimestampAttribute
  • ComplexTypeAttribute
  • ColumnAttribute
  • TableAttribute
  • InversePropertyAttribute
  • ForeignKeyAttribute
  • DatabaseGeneratedAttribute
  • NotMappedAttribute

 

Program.csに以下の名前空間と、処理を追加するとスキーマの再生成が可能です。

※といっても以下を実行するとデータが消えちゃうのでお気をつけて

using System.Data.Entity.Database;
DbDatabase.SetInitializer<ProductContext>(
new DropCreateDatabaseIfModelChanges<ProductContext>());

 

9.バリデーション

最後にアノテーションを利用したデータ検証を試してみましょう。

例えば、次のようにMinLength、MaxLength指定することで、文字列長の範囲を指定できます。

public class Supplier
{
    [Key]
    public string SupplierCode { get; set; }

    [MinLength(5)]
    [MaxLength(20)]
    public string Name { get; set; }
}

エラーが発生した時の処理を追加するため、Program.csに次の名前空間を追加します。

using System.Data.Entity.Validation;

Program.csに次の処理を追加して、実行するとエラーが発生します。これはSupplierのNameに値が3文字しか割り当てられておらず、データ検証で失敗してしまったことが原因です。

class Program
{
    static void Main(string[] args)
    {
        DbDatabase.SetInitializer<ProductContext>(
new DropCreateDatabaseIfModelChanges<ProductContext>()); using (var db = new ProductContext()) { var supplier = new Supplier { Name = "123" }; db.Suppliers.Add(supplier); try { db.SaveChanges(); } catch (DbEntityValidationException ex) { foreach (var failure in ex.EntityValidationErrors) { Console.WriteLine(
"{0} failed validation",
failure.Entry.Entity.GetType());
foreach (var error in failure.ValidationErrors) { Console.WriteLine(
"- {0} : {1}",
error.PropertyName,
error.ErrorMessage); } } } Console.WriteLine("Press any key to exit."); Console.ReadKey(); } } }

ということで、いろいろとCTP5 Code Firstの機能を試してみました。

気持ちよく、動かせたでしょうか?

 

Comments (0)

Skip to main content