Quantcast
Channel: C#タグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 9701

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

$
0
0
はじめに 現在、とあるプロジェクトでEntity Framework6を使用していています。 Entity Frameworkはなかなか便利なのですが、他のORマッパーと比較し、ちょいちょい独特な点があります。 (特に、最近までLaravelを中心に開発していたので、かなり使いにくく感じる点はあります) 特に厄介なのは、リレーション周り。 リレーションは、以下のように構成します。 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というメソッドを使用します。 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は任意リレーションです。 /// <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です。 // 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となります。 その他 細かいことはまた後ほど!

Viewing all articles
Browse latest Browse all 9701

Trending Articles