Entity Framework 實(shí)體框架的形成之旅--Code First的框架設(shè)計(jì)(5)

在前面幾篇介紹了Entity Framework 實(shí)體框架的形成過程征峦,整體框架主要是基于Database First的方式構(gòu)建既绩,也就是利用EDMX文件的映射關(guān)系,構(gòu)建表與表之間的關(guān)系,這種模式彈性好,也可以利用圖形化的設(shè)計(jì)器來設(shè)計(jì)表之間的關(guān)系接奈,是開發(fā)項(xiàng)目較多采用的模式,不過問題還是這個(gè)XML太過復(fù)雜通孽,因此有時(shí)候也想利用Code First模式構(gòu)建整個(gè)框架序宦。本文主要介紹利用Code First 來構(gòu)建整個(gè)框架的過程以及碰到的問題探討。

1背苦、基于SqlServer的Code First模式

為了快速了解Code First的工作模式挨厚,我們先以微軟自身的SQLServer數(shù)據(jù)庫進(jìn)行開發(fā)測(cè)試,我們還是按照常規(guī)的模式先構(gòu)建一個(gè)標(biāo)準(zhǔn)關(guān)系的數(shù)據(jù)庫糠惫,如下所示疫剃。



這個(gè)表包含了幾個(gè)經(jīng)典的關(guān)系,一個(gè)是自引用關(guān)系的Role表硼讽,一個(gè)是User和Role表的多對(duì)多關(guān)系巢价,一個(gè)是User和UserDetail之間的引用關(guān)系。
一般情況下固阁,能處理好這幾種關(guān)系壤躲,基本上就能滿足大多數(shù)項(xiàng)目上的要求了。這幾個(gè)表的數(shù)據(jù)庫腳本如下所示备燃。

create table dbo.Role (
   ID                   nvarchar(50)         not null,
   Name                 nvarchar(50)         null,
   ParentID             nvarchar(50)         null,
   constraint PK_ROLE primary key (ID)
)
go

create table dbo."User" (
   ID                   nvarchar(50)         not null,
   Account              nvarchar(50)         null,
   Password             nvarchar(50)         null,
   constraint PK_USER primary key (ID)
)
go

create table dbo.UserDetail (
   ID                   nvarchar(50)         not null,
   User_ID              nvarchar(50)         null,
   Name                 nvarchar(50)         null,
   Sex                  int                  null,
   Birthdate            datetime             null,
   Height               decimal              null,
   Note                 ntext                null,
   constraint PK_USERDETAIL primary key (ID)
)
go

create table dbo.UserRole (
   User_ID              nvarchar(50)         not null,
   Role_ID              nvarchar(50)         not null,
   constraint PK_USERROLE primary key (User_ID, Role_ID)
)
go

alter table dbo.Role
   add constraint FK_ROLE_REFERENCE_ROLE foreign key (ParentID)
      references dbo.Role (ID)
go

alter table dbo.UserDetail
   add constraint FK_USERDETA_REFERENCE_USER foreign key (User_ID)
      references dbo."User" (ID)
go

alter table dbo.UserRole
   add constraint FK_USERROLE_REFERENCE_ROLE foreign key (Role_ID)
      references dbo.Role (ID)
go

alter table dbo.UserRole
   add constraint FK_USERROLE_REFERENCE_USER foreign key (User_ID)
      references dbo."User" (ID)
go

我們采用剛才介紹的Code Frist方式來構(gòu)建實(shí)體框架碉克,如下面幾個(gè)步驟所示。
1)選擇來自數(shù)據(jù)庫的Code First方式并齐。



2)選擇指定的數(shù)據(jù)庫連接漏麦,并選擇對(duì)應(yīng)的數(shù)據(jù)庫表,如下所示(包括中間表UserRole)况褪。



生成項(xiàng)目后撕贞,項(xiàng)目工程會(huì)增加幾個(gè)類,包括Role實(shí)體類测垛,User實(shí)體類捏膨,UserDetail實(shí)體類(沒有中間表UserRole的實(shí)體類),還有一個(gè)是包含這些實(shí)體類的數(shù)據(jù)庫上下文關(guān)系食侮,它們的表之間的關(guān)系号涯,是通過代碼指定的,沒有了EDMX文件了锯七。
幾個(gè)類文件的代碼如下所示链快,其中實(shí)體類在類定義的頭部,增加了[Table("Role")]的說明起胰,表明了這個(gè)實(shí)體類和數(shù)據(jù)庫表之間的關(guān)系久又。
[Table("Role")]
public partial class Role
{
    public Role()
    {
        Children = new HashSet<Role>();
        Users = new HashSet<User>();
    }

    [StringLength(50)]
    public string ID { get; set; }

    [StringLength(50)]
    public string Name { get; set; }

    [StringLength(50)]
    public string ParentID { get; set; }

    public virtual ICollection<Role> Children { get; set; }

    public virtual Role Parent { get; set; }

    public virtual ICollection<User> Users { get; set; }
}

其他類如下所示。

[Table("User")]
public partial class User
{
    public User()
    {
        UserDetails = new HashSet<UserDetail>();
        Roles = new HashSet<Role>();
    }

    [StringLength(50)]
    public string ID { get; set; }

    [StringLength(50)]
    public string Account { get; set; }

    [StringLength(50)]
    public string Password { get; set; }

    public virtual ICollection<UserDetail> UserDetails { get; set; }

    public virtual ICollection<Role> Roles { get; set; }
}
[Table("UserDetail")]
public partial class UserDetail
{
    [StringLength(50)]
    public string ID { get; set; }

    [StringLength(50)]
    public string User_ID { get; set; }

    [StringLength(50)]
    public string Name { get; set; }

    public int? Sex { get; set; }

    public DateTime? Birthdate { get; set; }

    public decimal? Height { get; set; }

    [Column(TypeName = "ntext")]
    public string Note { get; set; }

    public virtual User User { get; set; }
}

還有一個(gè)就是生成的數(shù)據(jù)庫上下文的類效五。

public partial class DbEntities : DbContext
{
    public DbEntities() : base("name=Model1")
    {
    }
    public virtual DbSet<Role> Roles { get; set; }
    public virtual DbSet<User> Users { get; set; }
    public virtual DbSet<UserDetail> UserDetails { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Role>()
            .HasMany(e => e.Children)
            .WithOptional(e => e.Parent)
            .HasForeignKey(e => e.ParentID);

        modelBuilder.Entity<Role>()
            .HasMany(e => e.Users)
            .WithMany(e => e.Roles)
            .Map(m => m.ToTable("UserRole"));

        modelBuilder.Entity<User>()
            .HasMany(e => e.UserDetails)
            .WithOptional(e => e.User)
            .HasForeignKey(e => e.User_ID);

        modelBuilder.Entity<UserDetail>()
            .Property(e => e.Height)
            .HasPrecision(18, 0);
    }
}

上面這個(gè)數(shù)據(jù)庫上下文的操作類地消,通過在OnModelCreating函數(shù)里面使用代碼方式指定了幾個(gè)表之間的關(guān)系,代替了EDMX文件的描述畏妖。
這樣好像看起來比EDMX文件簡(jiǎn)單了很多脉执,感覺很開心,一切就那么順利戒劫。
如果我們使用這個(gè)數(shù)據(jù)庫上下文進(jìn)行數(shù)據(jù)庫的插入半夷,也是很順利的執(zhí)行,并包含了的多個(gè)表之間的關(guān)系處理迅细,代碼如下所示巫橄。

private void NormalTest()
{
    DbEntities db = new DbEntities();
    Role role = new Role() { ID = Guid.NewGuid().ToString(), Name = "test33" };

    User user = new User() { ID = Guid.NewGuid().ToString(), Account = "test33", Password = "test33" };
    UserDetail detail = new UserDetail() { ID = Guid.NewGuid().ToString(), Name = "userName33", Sex = 1, Note = "測(cè)試內(nèi)容33", Height = 175 };
    user.UserDetails.Add(detail);

    role.Users.Add(user);

    db.Roles.Add(role);
    db.SaveChanges();

    List<Role> list = db.Roles.ToList();
}

我們發(fā)現(xiàn),通過上面代碼的操作茵典,幾個(gè)表都寫入了數(shù)據(jù)湘换,已經(jīng)包含了他們之間的引用關(guān)系了。

2统阿、基于泛型的倉儲(chǔ)模式實(shí)體框架的提煉

為了更好對(duì)不同數(shù)據(jù)庫的封裝彩倚,我引入了前面介紹的基于泛型的倉儲(chǔ)模式實(shí)體框架的結(jié)構(gòu),希望后面能夠兼容多種數(shù)據(jù)庫的支持扶平,最終構(gòu)建代碼的分層結(jié)構(gòu)如下所示帆离。



使用這種框架的分層,相當(dāng)于為各個(gè)數(shù)據(jù)庫訪問提供了統(tǒng)一標(biāo)準(zhǔn)的通用接口结澄,為我們利用各種強(qiáng)大的基類快速實(shí)現(xiàn)各種功能提供了很好的保障哥谷。使用這種分層的框架代碼如下所示。

private void FrameworkTest()
{
    Role role = new Role() { ID = Guid.NewGuid().ToString(), Name = "test33" };

    User user = new User() { ID = Guid.NewGuid().ToString(), Account = "test33", Password = "test33" };
    UserDetail detail = new UserDetail() { ID = Guid.NewGuid().ToString(), Name = "userName33", Sex = 1, Note = "測(cè)試內(nèi)容33", Height = 175 };
    user.UserDetails.Add(detail);

    role.Users.Add(user);

    IFactory.Instance<IRoleBLL>().Insert(role);

    ICollection<Role> list = IFactory.Instance<IRoleBLL>().GetAll();

}

我們發(fā)現(xiàn)麻献,這部分代碼執(zhí)行的效果和純粹使用自動(dòng)生成的數(shù)據(jù)庫上下文DbEntities 來操作數(shù)據(jù)庫一樣呼巷,能夠?qū)懭敫鱾€(gè)表的數(shù)據(jù),并添加了相關(guān)的應(yīng)用關(guān)系赎瑰。

滿以為這樣也可以很容易擴(kuò)展到Oracle數(shù)據(jù)庫上王悍,但使用SQLServer數(shù)據(jù)庫生成的實(shí)體類,在Oracle數(shù)據(jù)庫訪問的時(shí)候餐曼,發(fā)現(xiàn)它生成的實(shí)體類名稱全部是大寫压储,一旦修改為Camel駝峰格式的字段,就會(huì)出現(xiàn)找不到對(duì)應(yīng)表字段的錯(cuò)誤源譬。

尋找了很多解決方案集惋,依舊無法有效避免這個(gè)問題,因?yàn)镺racle本身的表或者字段名稱是大小寫敏感的踩娘,關(guān)于Oracle這個(gè)問題刮刑,先關(guān)注后續(xù)解決吧喉祭,不過對(duì)于如果不考慮支持多種數(shù)據(jù)庫的話,基于SQLServer數(shù)據(jù)庫的Code First構(gòu)建框架真的還是比較方便雷绢,我們不用維護(hù)那個(gè)比較麻煩的EDMX文件泛烙,只需要在代碼函數(shù)里面動(dòng)態(tài)添加幾個(gè)表之間的關(guān)系即可。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末翘紊,一起剝皮案震驚了整個(gè)濱河市蔽氨,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌帆疟,老刑警劉巖鹉究,帶你破解...
    沈念sama閱讀 219,366評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異踪宠,居然都是意外死亡自赔,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門柳琢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來匿级,“玉大人,你說我怎么就攤上這事染厅《灰铮” “怎么了?”我有些...
    開封第一講書人閱讀 165,689評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵肖粮,是天一觀的道長孤页。 經(jīng)常有香客問我,道長涩馆,這世上最難降的妖魔是什么行施? 我笑而不...
    開封第一講書人閱讀 58,925評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮魂那,結(jié)果婚禮上蛾号,老公的妹妹穿的比我還像新娘。我一直安慰自己涯雅,他們只是感情好雄卷,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,942評(píng)論 6 392
  • 文/花漫 我一把揭開白布衬鱼。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪谎亩。 梳的紋絲不亂的頭發(fā)上驯绎,一...
    開封第一講書人閱讀 51,727評(píng)論 1 305
  • 那天垒酬,我揣著相機(jī)與錄音柑潦,去河邊找鬼。 笑死锈遥,一個(gè)胖子當(dāng)著我的面吹牛纫事,可吹牛的內(nèi)容都是我干的勘畔。 我是一名探鬼主播,決...
    沈念sama閱讀 40,447評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼丽惶,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼炫七!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起蚊夫,我...
    開封第一講書人閱讀 39,349評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎懦尝,沒想到半個(gè)月后知纷,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,820評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡陵霉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,990評(píng)論 3 337
  • 正文 我和宋清朗相戀三年琅轧,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片踊挠。...
    茶點(diǎn)故事閱讀 40,127評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡乍桂,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出效床,到底是詐尸還是另有隱情睹酌,我是刑警寧澤,帶...
    沈念sama閱讀 35,812評(píng)論 5 346
  • 正文 年R本政府宣布剩檀,位于F島的核電站憋沿,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏沪猴。R本人自食惡果不足惜辐啄,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,471評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望运嗜。 院中可真熱鬧壶辜,春花似錦、人聲如沸担租。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽奋救。三九已至阱洪,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間菠镇,已是汗流浹背冗荸。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留利耍,地道東北人蚌本。 一個(gè)月前我還...
    沈念sama閱讀 48,388評(píng)論 3 373
  • 正文 我出身青樓盔粹,卻偏偏與公主長得像,于是被迫代替她去往敵國和親程癌。 傳聞我的和親對(duì)象是個(gè)殘疾皇子舷嗡,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,066評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容