Entity Framework 6 Code First実践シリーズ(1):エンティティークラス構成-依存構成関係と関連付け


EFエンティティークラスの構成は、データ注釈またはFluent APIの2つの方法で構成できます.Fluent API構成の鍵は、エンティティークラスの依存関係を明らかにすることです.この方法で構成すると、迅速かつ効率的で合理的です.理解を容易にするために,簡略化されたエンティティAとB,およびA,Bの構成クラスAMapとBMapを用いて,エンティティクラス関係を正しく構成する方法を実証した.
public class A
{
    public int Id { get; set; }
}

public class B
{
    public int Id { get; set; }
}

public class AMap : EntityTypeConfiguration<A>
{
    public AMap()
    {
        this.HasKey(o => o.Id);
    }
}

public class BMap : EntityTypeConfiguration<B>
{
    public BMap()
    {
        this.HasKey(o => o.Id);
    }
}

 

一、依存関係を確定する:
エンティティBがエンティティA(B->A)に依存していると仮定すると、エンティティBにはエンティティAへの参照が存在する.
二、実体クラスの構成はどこに書くべきですか.
BがA(B−>A)に依存すると仮定すると,Bテーブルに外部キー(Aテーブルのプライマリキー値)を生成することが望ましいことは明らかである.以下の2つの方法で同じテーブル構造を実現することができるが、BのプロファイルBMapでリレーショナル構成を行うべきであることは間違いない.
(1)BはAに依存し,AはBの存在を知らないことができる.
(2)Aは単独で存在してもよく,構成書がどこに書いてもAテーブルに影響を及ぼさない.
(3)B対Aの依存は,Bテーブルに外部キー(Aテーブルの主キー)を生成することである.
おすすめの書き方:
public class BMap : EntityTypeConfiguration<B> { public BMap() { this.HasRequired(o => o.A).WithMany(o=>o.ListB); } }
 :
public class AMap : EntityTypeConfiguration<A> { public AMap() { this.HasMany(o => o.ListB).HasRequired(o => o.A); } }

依存の方向によって使用される構成が決定されます.これは、エンティティ・クラスの数と関係が複雑な場合に特に重要です.10個のエンティティ・クラスがAに依存していると仮定すると、ブレンド・ライト構成は明らかに望ましくありませんが、依存エンティティで構成された結果、Aのプロファイルが頻繁に変更され、クラスAのプロファイルがAテーブルの変化を引き起こすとは確信できません.
三、依存関係の構成
プロファイルのベースクラスEntityTypeコンフィギュレーションには、エンティティクラスを構成するための一連のHasメソッドが含まれています.HasOptionalとHasRequiredは、エンティティの参照属性に基づいてエンティティ関係を構成します.BがA(B−>A)に依存すると仮定すると、HasOptionalはBが単独で存在することを可能にし、Bテーブルに空の外部キーを生成する.HasRequiredでは、Bが単独で存在することは許可されません.これにより、Bテーブルに空でない外部キーが生成されます.
public class BMap : EntityTypeConfiguration<B>
{
    public BMap()
    {
        this.HasKey(o => o.Id);
        this.HasRequired(o => o.A);
    }
}

HasRequired
public class BMap : EntityTypeConfiguration<B>
{
    public BMap()
    {
        this.HasKey(o => o.Id);
        this.HasOptional(o => o.A);
    }
}

HasOptional

四、関連タイプの構成
HasOptionalオブジェクトとHasRequiredオブジェクトは、それぞれO p t i o n a l N avigationPropertyConfigurationオブジェクトとR e q i r e d N avigationPropertyConfigurationオブジェクトを返し、WithManyオブジェクトとWithOptionalオブジェクトを使用して関連するタイプを構成します.
A:B=1:Nの場合、WithManyを使用します.
public class BMap : EntityTypeConfiguration<B>
{
    public BMap()
    {
        this.HasKey(o => o.Id);
        this.HasOptional(o => o.A).WithMany();
    }
}

1:N( )
public class BMap : EntityTypeConfiguration<B>
{
    public BMap()
    {
        this.HasKey(o => o.Id);
        this.HasRequired(o => o.A).WithMany();
    }
}

1:N( )

A:B=1:1の場合、WithOptionalを使用します.1:1の関連付けには、テーブルBの外部キーをプライマリ・キーとして実装する外部キーの非空および一意性が必要です.
public class BMap : EntityTypeConfiguration<B>
{
    public BMap()
    {
        this.HasKey(o => o.Id);
        this.HasRequired(o => o.A).WithOptional();
    }
}

四、オプションナビゲーション属性
ナビゲーション・プロパティは、関連付けタイプによって決まりますが、エンティティの依存関係や関連付けタイプには影響しません.
B->Aの場合、A:B=1:Nの場合、ICollectionタイプのナビゲーション属性をAに追加し、リレーションシップ構成を変更してWithManyメソッドに渡すことができます.
public class A
{
    public int Id { get; set; }

    public ICollection<B> BList { get; set; }
}

public class B
{
    public int Id { get; set; }

    public A A { get; set; }
}

public class AMap : EntityTypeConfiguration<A>
{
    public AMap()
    {
        this.HasKey(o => o.Id);
    }
}

public class BMap : EntityTypeConfiguration<B>
{
    public BMap()
    {
        this.HasKey(o => o.Id);
        this.HasOptional(o => o.A).WithMany(o => o.BList);
    }
}

A:B=1:1の場合、AにBタイプのナビゲーション属性を追加し、リレーションシップ構成を変更してWithOptionalメソッドに渡すことができます.
public class A
{
    public int Id { get; set; }

    public B B { get; set; }
}

public class B
{
    public int Id { get; set; }

    public A A { get; set; }
}

public class AMap : EntityTypeConfiguration<A>
{
    public AMap()
    {
        this.HasKey(o => o.Id);
    }
}

public class BMap : EntityTypeConfiguration<B>
{
    public BMap()
    {
        this.HasKey(o => o.Id);
        this.HasRequired(o => o.A).WithOptional(o => o.B);
    }
}

五、明示的外部キー属性
B->Aの場合、A:B=1:1の場合、外部キーはプライマリキーです.
A:B=1:Nの場合、ナビゲーション属性に対応する外部キー属性をカスタマイズできます.まず、Bに外部キー用の明示的な属性を追加します.
public class B
{
    public int Id { get; set; }

    public A A { get; set; }

    //public int AId { get; set; }
    public int? AId { get; set; }
}

WithManyはD e p e n d e n t N a v i g a tionPropertyConfigurationオブジェクトを返します.このオブジェクトのHasForeignKeyメソッドを使用します.エンティティのコンタクトがHasOptionalに構成されている場合は、空のタイプマッチングを使用する必要があります.
public class BMap : EntityTypeConfiguration<B>
{
    public BMap()
    {
        this.HasKey(o => o.Id);
        this.HasOptional(o => o.A).WithMany().HasForeignKey(o => o.AId);
    }
}

六、カスケード削除構成
HasForeignKeyは、CascadableNavigationPropertyConfigurationオブジェクトを返します.EFはデフォルトでカスケード削除をオンにします.エンティティ関係が複雑でカスケード削除をオンにできない場合、このオブジェクトのWillCascadeOnDeleteメソッドを使用して、カスケード削除をキャンセルするように構成します.
七、双方向依存について
EFにおけるエンティティの関連付けは表の外部キーによって実現され,1:Nも1:1も外部キーによって実現される.1:N構成に基づいて双方向依存テーブルを構成することができるが,通常,いわゆる多対多は双方向依存ではない.例えば、ユーザーとロール、学生とカリキュラム、文章とラベルなど、まったく依存していない.両者は独立して存在することができ、関係の依存をマッピングするだけで、これは1:Nの問題である.
現在のエンティティータイプコンフィギュレーションを使用して、ToTable、HasKeyなどのインスタンスメソッドが現在のエンティティークラスマッピングのTableを構成するエンティティ依存性を構成します.HasRequiredメソッドとHasOptionalメソッドも対応するTableに外部キーを生存させ、HasManyメソッドはその中の異種であり、あいにく構成されている非現在のエンティティクラスである.
HasMany、WithManyは、双方向参照の構成時にリレーショナル・テーブルを自動的に生成するだけでなく、構成の混乱をもたらします.自動生成関係テーブルとは,我々の実体クラスとTableの一対一の対応を打ち破ったものである.MicrosoftでAspNet.Identity.EntityFramework 1.0では、IdentityUserとIdentityRoleが双方向参照によって自動的にリレーショナル・テーブルを生成するのではなく、IdentityUserRoleエンティティ・クラスを定義してマッピングされていることがわかります.IdentityDbContextのOnModelCreating構成では、HasMany構成TUserのRolesプロパティが使用されているが、IdentityUserRoleで完全に構成できることがわかります.2.0バージョンでもそうです.
八、一般的な構成例:
1.ユーザーとロール:
(1)依存関係の決定:UserとRoleは単独で存在してもよいが、UserRoleは単独で存在してはならないので、存在する依存はUserRole->User、UserRole->Roleである.
(2)依存関係の構成:UserRoleは単独では存在しないため,HasRequiredを用いる.
(3)関連タイプの決定:User:UserRole==1:*;Role:UserRole=1:*のため、WithManyを使用します.
(4)明示的な外部キー属性:明示的な外部キー属性としてUserRoleにUserIdとRoleIdを追加します.
(5)オプションのナビゲーション属性:ICollectionタイプのナビゲーション属性をUserとRoleに追加します.
UserRoleに重複するユーザーロールマッピングは存在しないため、結合プライマリ・キーとして外部キーを使用します.
public class User
{
    public User()
    {
        this.UserRoles = new List<UserRole>();
    }

    public int Id { get; set; }

    public string UserName { get; set; }

    public ICollection<UserRole> UserRoles { get; set; }
}

public class Role
{
    public Role()
    {
        this.UserRoles = new List<UserRole>();
    }

    public int Id { get; set; }

    public string RoleName { get; set; }

    public ICollection<UserRole> UserRoles { get; set; }
}

public class UserRole
{
    public User User { get; set; }

    public int UserId { get; set; }

    public Role Role { get; set; }

    public int RoleId { get; set; }
}

public class UserRoleMap : EntityTypeConfiguration<UserRole>
{
    public UserRoleMap()
    {
        this.HasKey(o => new { o.UserId, o.RoleId });
        this.HasRequired(o => o.User).WithMany(o => o.UserRoles).HasForeignKey(o => o.RoleId);
        this.HasRequired(o => o.Role).WithMany(o => o.UserRoles).HasForeignKey(o => o.UserId);
    }
}

2.ノードツリー:
(1)依存関係の特定:Category自己依存,Category->Category
(2)構成依存関係:Categoryは単独で存在するためHasOptionalを用いる.
(3)関連タイプ:Category:Category=1:*を決定するため、WithManyを使用する.
(4)明示的な外部キー属性:Categoryが単独で存在できるため、UserRoleにParentIdを追加します.ParentIdは空のタイプです.
(5)オプションのナビゲーション属性:ICollectionタイプのナビゲーション属性をCategoryに追加します.
public class Category
{    public int Id { get; set; }    public string Name { get; set; }    public int? ParentId { get; set; }    public Category Parent { get; set; }    public ICollection<Category> Children { get; set; }
}public class CategoryMap : EntityTypeConfiguration<Category>{    public CategoryMap()
    {        this.HasKey(o => o.Id);        this.HasOptional(o => o.Parent).WithMany(o => o.Children).HasForeignKey(o => o.ParentId);
    }
}