【Entity Framework6】HasRequiredとHasOptionalの使い分け方【リレーション】

# はじめに
現在、とあるプロジェクトでEntity Framework6を使用していています。
Entity Frameworkはなかなか便利なのですが、他のORマッパーと比較し、ちょいちょい独特な点があります。
（特に、最近までLaravelを中心に開発していたので、かなり使いにくく感じる点はあります）

特に厄介なのは、リレーション周り。
リレーションは、以下のように構成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/204834/0d85d1b5-7749-7020-968b-37096ca51d35.png)

ContextのOnModelCreatingに、リレーションを一通り書くことになります。
ここで、1:nのリレーションの書き方で、かなり迷うことになりました。

いまだにEFCoreではなく、EF6を使用している方は少ないかもしれませんが、ここではそんな希少価値の高い方々向けに、
リレーション周りについて、まとめていくことにしました。


## 例のテーブル構成

```
CREATE TABLE [dbo].[テーブル11](
	[主キー] [int] PRIMARY KEY NOT NULL,
	[値] [nvarchar](128) NULL
);

CREATE TABLE [dbo].[テーブル12](
	[主キー] [int] PRIMARY KEY NOT NULL,
	[外部キー] [int] NOT NULL,
	[値] [nvarchar](128) NULL
);


CREATE TABLE [dbo].[テーブル13](
	[主キー] [int] PRIMARY KEY NOT NULL,
	[外部キー] [int] NULL,
	[値] [nvarchar](128) NULL
);




CREATE TABLE [dbo].[テーブル31](
	[主キー] [int] PRIMARY KEY NOT NULL,
	[値] [nvarchar](128) NULL
);

CREATE TABLE [dbo].[テーブル32](
	[主キー] [int]  NOT NULL,
	[連番] [int] NOT NULL,
	[値] [nvarchar](128) NULL,
	primary key (主キー, 連番)
);


```

## ER図
そのうち


## HasRequiredとHasOptionalの使い分け
1:nリレーションを構築する際に、HasRequiredもしくはHasOptionalというメソッドを使用します。

``` csharp
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);

            modelBuilder.Entity<Table12>() // 子テーブル
              .HasRequired(x => x.Table11) // 親テーブルへの参照
              .WithMany(x => x.Table12s) // 子テーブルへの参照
              .HasForeignKey(x => new { x.Gaibuki }); // 子テーブル側の、外部キーの設定

            modelBuilder.Entity<Table23>() // 子テーブル
              .HasOptional(x => x.Table21) // 親テーブルへの参照
              .WithMany(x => x.Table23s) // 子テーブルへの参照
              .HasForeignKey(x => new { x.Gaibuki }); // 子テーブル側の、外部キーの設定
        }
```

このメソッドの使い分けについて、記載していきます。

### HasRequired：そのキーが必須の場合　HasOptional：そのキーが必須でない場合
1:nのリレーションを行う場合でも、以下のようなケースがあると思います。

- 必ず、1:nの親子関係が組まれる場合。例：「契約」と「契約明細」という1:nテーブルがある場合、「契約」の外部キーをNULLとして「契約明細」のデータが作成されることは無い(契約明細のみ単独で作ることは無い)ので、「契約」への外部キーは必須となる。

- 1:nの親子関係が組まれないケースがある場合。例：「契約」というテーブルに「削除ユーザー」という列があり、削除ユーザーのIDを入れることによって、「ユーザー」テーブルへのリレーションが行われる場合。削除されている場合のみidをセットするため、必須ではない。

このように、「nは、1へのリレーションが必ず行われる場合」と「nは、1へのリレーションが無くてもデータ作成が可能な場合」の2種類あることが分かります。
この使い分けが、HasRequiredとHasOptionalの使い分けです。

### 制約
#### HasRequiredを使用する場合
・外部キーに設定した列は、null非許容型である必要がある
→int?のようなnull許容型は使用できません。なぜなら、親テーブルへの参照が必須となるためです。
よって、intのようなnull非許容型である必要があります。

#### HasOptionalを使用する場合
・外部キーに設定した列は、null許容型である必要がある？
→int?のようなnull許容型が使用できます。また、ここは要調査なのですが、intのようなnull非許容型も、使用できるかもしれません。

・外部キーに設定した列に、Required属性は設定できない
→外部キーに設定した列に、Required属性は設定できないようです。

### 例
以下のようなModel構成があったとします。
テーブル12は必須リレーション、テーブル13は任意リレーションです。

``` csharp
 /// <summary>
    /// テーブル11
    /// </summary>
    [Table("テーブル11")]
    [Serializable]
    public partial class Table11
    {
        /// <summary>
        /// 主キー
        /// </summary>
        [Column("主キー")]
        [Display(Name = "主キー")]
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.None)]
        public int? Syuki { get; set; }

        /// <summary>
        /// 値
        /// </summary>
        [Column("値")]
        [Display(Name = "値")]
        public string Atai { get; set; }

        public virtual ICollection<Table12> Table12s { get; set; }
        public virtual ICollection<Table13> Table13s { get; set; }
    }

    /// <summary>
    /// テーブル12
    /// </summary>
    [Table("テーブル12")]
    [Serializable]
    public partial class Table12
    {
        /// <summary>
        /// 主キー
        /// </summary>
        [Column("主キー")]
        [Display(Name = "主キー")]
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.None)]
        public int? Syuki { get; set; }

        /// <summary>
        /// 外部キー
        /// </summary>
        [Column("外部キー")]
        [Display(Name = "外部キー")]
        public int Gaibuki { get; set; }

        /// <summary>
        /// 値
        /// </summary>
        [Column("値")]
        [Display(Name = "値")]
        public string Atai { get; set; }

        public virtual Table11 Table11 { get; set; }
    }

    /// <summary>
    /// テーブル13
    /// </summary>
    [Table("テーブル13")]
    [Serializable]
    public partial class Table13
    {
        /// <summary>
        /// 主キー
        /// </summary>
        [Column("主キー")]
        [Display(Name = "主キー")]
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.None)]
        public int? Syuki { get; set; }

        /// <summary>
        /// 外部キー(null許可)
        /// </summary>
        [Column("外部キー")]
        [Display(Name = "外部キー")]
        public int? Gaibuki { get; set; }

        /// <summary>
        /// 値
        /// </summary>
        [Column("値")]
        [Display(Name = "値")]
        public string Atai { get; set; }

        public virtual Table11 Table11 { get; set; }
    }

```

この場合、以下のようなリレーション設定となります。
テーブル12は必須リレーションなのでHasOptionalはOK、テーブル13は任意リレーションなのでHasOptionalはOKです。

``` csharp
            // OK
            modelBuilder.Entity<Table12>()
              .HasRequired(x => x.Table11)
              .WithMany(x => x.Table12s)
              .HasForeignKey(x => new { x.Gaibuki });

            // ダメ
            //modelBuilder.Entity<Table12>()
            //  .HasOptional(x => x.Table11)
            //  .WithMany(x => x.Table12s)
            //  .HasForeignKey(x => new { x.Gaibuki });


            // OK
            modelBuilder.Entity<Table13>()
              .HasOptional(x => x.Table11)
              .WithMany(x => x.Table13s)
              .HasForeignKey(x => new { x.Gaibuki });

            // これもOK
            //modelBuilder.Entity<Table13>()
            //  .HasRequired(x => x.Table11)
            //  .WithMany(x => x.Table13s)
            //  .HasForeignKey(x => new { x.Gaibuki });
```


### 1:nのn側のテーブルが、複合キーの場合は、HasRequired
1:nのn側のテーブルで、以下のような複合主キー設定が行われている場合があります。今回の例でいうと、テーブル32が該当します。

- 1側のテーブルの主キー
- 連番

この場合、外部キーは「1側のテーブルの主キー」になります。
そしてこの場合、この主キーには値が必ず入ってくるので、HasRequiredとなります。


## その他
細かいことはまた後ほど！