【轉】Entity Framework 6 Code First 實踐系列(1):實體類配置-根據依賴配置關係和關聯

本文轉自:http://www.cnblogs.com/easygame/p/3622893.htmlhtml

EF實體類的配置可使用數據註釋或Fluent API兩種方式配置,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);
    }
}
實體類配置

1、肯定依賴關係:ide

假設實體B依賴於實體A(B->A),那麼實體B中存在對實體A的引用。ui

2、實體類配置應該寫在哪裏?this

假設B依賴於A(B->A),很顯然,咱們但願的是B表中生成外鍵(A表的主鍵值)。如下兩種方式均可以實現相同的表結構,但毫無疑問咱們應該在B的配置文件BMap中進行關係配置。spa

(1)B依賴於A,A能夠對B的存在一無所知。code

(2)A能夠單獨存在,配置寫在哪裏都不會對A表產生影響。htm

(3)B對A的依賴是經過在B表中生成外鍵(A表的主鍵)。對象

推薦的寫法:blog

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表的變化。

3、配置依賴關係

配置文件的基類EntityTypeConfiguration包含了一系列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

4、配置關聯類型

HasOptional和HasRequired分別返回OptionalNavigationPropertyConfiguration和RequiredNavigationPropertyConfiguration對象,咱們使用其中的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();
    }
}
1:1

4、可選導航屬性

導航屬性由關聯類型決定,但其存在與否不會影響實體的依賴關係和關聯類型。

對於B->A,若是A:B = 1:N,咱們能夠在A中添加ICollection<B>類型的導航屬性,同時修改關係配置,將該屬性傳遞給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);
    }
}
導航屬性

5、顯式外鍵屬性

對於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返回DependentNavigationPropertyConfiguration對象,咱們使用該對象的HasForeignKey方法,若是實體聯繫配置爲HasOptional,則須要使用可空類型匹配。

public class BMap : EntityTypeConfiguration<B>
{
    public BMap()
    {
        this.HasKey(o => o.Id);
        this.HasOptional(o => o.A).WithMany().HasForeignKey(o => o.AId);
    }
}
外鍵配置

6、級聯刪除配置

HasForeignKey返回CascadableNavigationPropertyConfiguration對象,EF默認開啓級聯刪除,當實體關係複雜致使沒法開啓級聯刪除時,咱們使用該對象的WillCascadeOnDelete方法配置取消級聯刪除。

7、關於雙向依賴

EF中實體的關聯經過表的外鍵實現,1:N仍是1:1都是經過外鍵實現。咱們能夠根據1:N配置的方式配置出雙向依賴的表,但一般所謂的多對多都不是雙向依賴。例如用戶和角色、學生和課程、文章和標籤等,甚至根本沒有依賴,由於兩者均可以獨立存在,有的只是映射關係對兩者的依賴,而這是1:N的問題。

咱們使用EntityTypeConfiguration配置實體依賴,該類的ToTable、HasKey等實例方法都用於配置當前實體類映射的Table。HasRequired和HasOptional方法也會在對應的Table中生存外鍵,而HasMany方法則是其中的異類,恰恰配置的非當前實體類。

HasMany、WithMany除了在配置雙向引用時替咱們自動生成關係表,帶來更多的是配置混亂。而所謂的自動生成關係表更是打破了咱們實體類和Table的一一對應。在Microsoft.AspNet.Identity.EntityFramework 1.0中,咱們能夠看到IdentityUser和IdentityRole並無經過雙向引用自動生成關係表,而是定義了IdentityUserRole實體類用來映射:經過IdentityDbContext<TUser>的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)可選的導航屬性:在User和Role中添加ICollection<UserRole>類型的導航屬性。

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);
    }
}
UserRole

2.節點樹:

(1)肯定依賴關係:Category自依賴,Category->Category

(2)配置依賴關係:Category能夠單獨存在,所以使用HasOptional。

(3)肯定關聯類型:Category:Category==1:*,所以使用WithMany。

(4)顯式的外鍵屬性:在UserRole中添加ParentId,因爲Category能夠單獨存在,ParentId爲可空類型。

(5)可選的導航屬性:在Category中添加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);
    }
}
Category->Category
相關文章
相關標籤/搜索