こんにちは。
テックリードのTerukiです。
今まで長いことEF Coreを使って開発をしていますが、知らない間に増えた属性があることに最近気付いたのでまとめて見ようと思います。
前置き
今まではFluent APIでしか指定することが出来なかった項目がいくつかありましたが、今現在ではHasOneやWithMany等の詳細なナビゲーション指定を除くほとんどの項目が属性経由で指定できるようになっています。
混在している状況は良くないので、プロジェクトによってどちらかに統一をするのが良いと思います。
Key属性
主キーを指定できる属性です。が、後述するPrimaryKey属性を使うほうが分かりやすいと思うので、新規で使うのはやめたほうが良さそうです。
この属性はプロパティに対して指定する関係上、複合主キーを設定するためには使用できません。
[Key]
public ulong Id { get; set; }
PrimaryKey属性
EF Core 7で追加された属性です。
Key属性とは違い、こちらはクラスに対して指定します。第2引数以降にプロパティ名を渡すことで複合主キーにすることもできます。
Key属性と違って明示的にPrimaryと書いてあるので直感的で良いですね。
[PrimaryKey(nameof(Key1), nameof(Key2))]
public class User {
public string Key1 { get; set; }
public string Key2 { get; set; }
}
Table属性
テーブル名を指定できる属性です。
スキーマも指定できますが、使う場面はほとんどないかもしれません。
[Table("users")]
public class User {
public string Key1 { get; set; }
public string Key2 { get; set; }
}
Column属性
カラムに対して何か変更を加えたい時に指定します。
カラム名を自動生成のものとは別のものにしたかったり、データベース上の型を変更したい時に使えます。変な使い方をしない限りは基本は使わなくて良いかなと。
[Column("user_id", TypeName = "text")]
public string UserId { get; set; }
StringLength属性、MaxLength属性
この属性をカラムに付けるとvarcharの長さを指定できます。
StringLengthはMinimumLengthも指定できる属性ですが、EF Coreにおいては無視されます。
[MaxLength(255)]
public string UserId { get; set; }
ForeignKey属性
外部キーを設定できます。
この場合、UserIdカラムを持つテーブルからIncludeを使ったJOINができます。
一方向のリレーションの定義はできますが、双方向にナビゲーションプロパティを定義するならHasOne, WithOne等のFluent APIによる記述が必要です。
しっかり書いておくとIncludeが便利なので積極的に使うと良さそうです。
濫用には注意。
[MaxLength(255)]
public string UserId { get; set; }
[ForeignKey(nameof(UserId)]
public User? User { get; set; }
Index属性
こちらもPrimaryKey属性と同様に、複数のプロパティを並べることで複合インデックスを指定できます。
IsUniqueプロパティもあるのでユニークインデックスとするかどうかも一緒に指定できます。
他に、AllDescendingという降順にインデックスを付与する指定もありますが、あまりお世話になる機会はないかもしれません。
[Index(nameof(UserId), nameof(CreatedAt), IsUnique = true)]
public class User {
public string UserId { get; set; }
public DateTimeOffset CreatedAt { get; set; }
}
DeleteBehavior属性
この属性もPrimaryKeyと同じEF Core 7から追加されたものです。
外部キー制約の付与されたレコードを削除時の挙動を指定できます。
EF Core 7以前はFluent APIでしか指定することはできませんでした。
public string UserId { get; set; }
[DeleteBehavior(DeleteBehavior.NoAction)]
public User? User { get; set; }
個人的にはコードファーストなプロジェクトであればFluent APIよりもEntityクラス側にすべてを記載してしまうほうが、後々確認する時に便利だなと思っています。
Fluent APIの場合、DbContextが肥大化していると定義がどこにあるのか探すのにちょっと手間ですし、Entityクラス側に書かれていればIDEで直接そのEntityクラスを開けばすぐに分かるのが良いですね。
いろいろ書いてみましたが、プロジェクトの方針に従うのが一番なので不明な点はコードオーナーに確認してみると良いと重います。