しばやん雑記

Azure とメイドさんが大好きなフリーランスのプログラマーのブログ

Entity Framework コードファーストで複合キーを使ってみる

IDbSet に定義されてる Find というメソッドがオブジェクトの配列を受け取るようになっていて、複合キーの時に複数の値を指定できるようになってるんだなーと思っていたのですが、実際に使ったことが無かったので試しました。

とりあえず適当にモデルクラスを作ります。複合キーを定義する時には Key 属性を使って明示的に指定してあげましょう。

public class OrderDetail
{
    [Key]
    public int ProductId { get; set; }

    [Key]
    public int OrderId { get; set; }

    public decimal UnitPrice { get; set; }
}

これで複合キーを持つモデルクラスが完成しましたので、データコンテキストなどは適当に作成してデータベースに追加してみます。

var context = new ProductContext();

context.OrderDetails.Add(new OrderDetail
{
    ProductId = 10,
    OrderId = 5,
    UnitPrice = 1999
});

context.SaveChanges();

これで上手く動きそうですが、実際に試すと以下のような例外が投げられてエンティティの追加に失敗してしまいました。

表示されたポップアップのメッセージを読むと ColumnAttribute か HasKey を使ってカラムの順番を決めろと書いてありました。ちなみに HasKey は Fluent API なので、今回は ColumnAttribute をモデルクラスに追加して順番を決めてあげましょう。

public class OrderDetail
{
    // Order = 0
    [Key]
    [Column(Order = 0)]
    public int ProductId { get; set; }

    // Order = 1
    [Key]
    [Column(Order = 1)]
    public int OrderId { get; set; }

    public decimal UnitPrice { get; set; }
}

これで再度実行すると正常にエンティティが追加されました。やったね。

追加は出来たので、次はエンティティの取得を行ってみたいと思います。当然ながら IDbSet に用意された Find メソッドを使います。先程 0 オリジンでカラムの順番を決めた理由がここにあります。

// 引数に指定する値の順番は Column の Order で指定した順にしないと駄目
var orderDetail = context.OrderDetails.Find(10, 5);

当然ですが Find に指定する値の数はプライマリキーの数と同じじゃないとダメです。そうじゃないとユニークなエンティティが取得できないからですね。

複合キーを一番使いそうな多対多関係時の中間テーブルは、Entity Framework だと自動的に生成されるのであまり使う場面はなさそうです。しかし、いざ必要になった時にすぐに使えないのは困ると思うので、頭の片隅にでも入れておいて貰えれば嬉しいですね。