パート 2: ドメイン モデルの作成

作成者: Rick Anderson

完成したプロジェクトをダウンロードする

モデルを追加する

Entity Framework へのアプローチ方法として、次の 3 つが挙げられます。

  • データベースファースト: データベースから始め、Entity Framework によってコードが生成されます。
  • モデルファースト: ビジュアル モデルから始め、Entity Framework によってデータベースとコードの両方が生成されます。
  • コードファースト: コードから始め、Entity Framework によってデータベースが生成されます。

コードファーストのアプローチを使用しているため、まずドメイン オブジェクトを POCO (プレーンな CLR オブジェクト) として定義します。 コードファースト アプローチでは、ドメイン オブジェクトに、トランザクションや永続化など、データベース レイヤーをサポートするために追加のコードを必要としません (具体的には、EntityObject クラスから継承する必要がありません)。引き続きデータ注釈を使用して、Entity Framework でのデータベース スキーマの作成方法を制御することはできます。

POCO にはデータベースの状態を記述する追加のプロパティがないため、JSON や XML に簡単にシリアル化できます。 ただし、このチュートリアルの後半で説明するように、Entity Framework モデルを常にクライアントに直接公開する必要があるわけではありません。

次の POCO を作成します。

  • Product
  • 注文
  • OrderDetail

各クラスを作成するには、ソリューション エクスプローラーの Models フォルダーを右クリックします。 コンテキスト メニューの [追加] を選択し、[クラス] を選択します。

Screenshot of the Solutions Explorer menu for the Models folder. The Add menu is open and the Class option is highlighted.

次の実装を使用して、Product クラスを追加します。

namespace ProductStore.Models
{
    using System.ComponentModel.DataAnnotations;

    public class Product
    {
        [ScaffoldColumn(false)]
        public int Id { get; set; }
        [Required]
        public string Name { get; set; }
        public decimal Price { get; set; }
        public decimal ActualCost { get; set; }
    }
}

慣例により、Entity Framework では Id プロパティを主キーとして使用し、それをデータベース テーブルの ID 列にマップします。 新しい Product インスタンスを作成すると、データベースによって値が生成されるため、Id の値は設定しません。

ScaffoldColumn 属性は、エディター フォームの生成時に Id プロパティをスキップするよう、ASP.NET MVC に指示します。 Required 属性は、モデルの検証に使用されます。 Name プロパティが空でない文字列である必要があることを指定します。

Order クラスを追加します。

namespace ProductStore.Models
{
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;

    public class Order
    {
        public int Id { get; set; }
        [Required]
        public string Customer { get; set; }

        // Navigation property
        public  ICollection<OrderDetail> OrderDetails { get; set; }
    }
}

OrderDetail クラスを追加します。

namespace ProductStore.Models
{
    public class OrderDetail
    {
        public int Id { get; set; }
        public int Quantity { get; set; }
        public int OrderId { get; set; }
        public int ProductId { get; set; }

        // Navigation properties
        public Product Product { get; set; }
        public Order Order { get; set; }
    }
}

外部キーの関係

1 つの注文には多くの注文の詳細が含まれており、注文の詳細はそれぞれ 1 つの製品を参照します。 これらの関係を表すために、OrderDetail クラスでは OrderIdProductId という名前のプロパティを定義します。 Entity Framework では、これらのプロパティが外部キーを表すと推測し、外部キー制約をデータベースに追加します。

Screenshot of Visual Studio menus for the Orders, Products, and OrderDetails classes.

また、Order クラスと OrderDetail クラスには、関連オブジェクトへの参照を含む "ナビゲーション" プロパティもあります。 注文が指定されると、ナビゲーション プロパティに従って、その注文内の製品に移動できます。

次はプロジェクトをコンパイルします。 Entity Framework ではリフレクションを使用してモデルのプロパティを検出するため、データベース スキーマを作成するにはコンパイル済みアセンブリが必要です。

メディアタイプ フォーマッタを構成する

メディアタイプ フォーマッタは、Web API によって HTTP 応答本文が書き込まれるときにデータをシリアル化するオブジェクトです。 組み込みのフォーマッタでは、JSON と XML の出力がサポートされます。 既定では、これらのフォーマッタの両方で、すべてのオブジェクトで値によるシリアル化が行われます。

値によるシリアル化では、オブジェクト グラフに循環参照が含まれている場合に問題が発生します。 これはまさしく Order クラスと OrderDetail クラスで当てはまります。それぞれが互いへの参照を保持しているためです。 フォーマッタは参照に従い、値により各オブジェクトを書き込み、堂々巡りとなります。 そのため、既定の動作を変更する必要があります。

ソリューション エクスプローラーで、App_Start フォルダーを展開し、WebApiConfig.cs という名前のファイルを開きます。 以下のコードを WebApiConfig クラスに追加します。

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );

        // New code:
        var json = config.Formatters.JsonFormatter;
        json.SerializerSettings.PreserveReferencesHandling =
            Newtonsoft.Json.PreserveReferencesHandling.Objects;

        config.Formatters.Remove(config.Formatters.XmlFormatter);
    }
}

このコードにより、オブジェクト参照を保持するように JSON フォーマッタが設定され、パイプラインから XML フォーマッタが完全に削除されます (オブジェクト参照を保持するように XML フォーマッタを構成できますが、少し手間がかかり、このアプリケーションに必要なのは JSON のみです。詳細については、「循環オブジェクト参照の処理」を参照してください)。