很久沒有寫博客了,一些讀者也經(jīng)常問問一些問題,不過最近我確實(shí)也很忙,除了處理日常工作外,平常主要的時(shí)間也花在了繼續(xù)研究微軟的實(shí)體框架(EntityFramework)方面了。這個(gè)實(shí)體框架加入了很多特性(例如LINQ等),目前也已經(jīng)應(yīng)用的比較成熟了,之所以一直沒有整理成一個(gè)符合自己開發(fā)模式的實(shí)體框架,是因?yàn)檫@個(gè)框架和原來我的基于EnterpriseLibrary的模式還是有很大的不同,不過實(shí)體框架推出來也很久了,目前也去到了EntityFramework6了,聽說7也快出來了。
隨著我自己參考閱讀了大量的項(xiàng)目源碼以及對(duì)實(shí)體框架各個(gè)技術(shù)點(diǎn)的學(xué)習(xí)深入,對(duì)其中很多的方面都有自己的一些見解和心得,希望通過這個(gè)系列,能夠和讀者一步步分析,一步步深入學(xué)習(xí)這個(gè)微軟目前最為流行的.NET開發(fā)框架。本篇主要從基礎(chǔ)開始一步步介紹基于泛型的倉(cāng)儲(chǔ)模式實(shí)體框架(The Entity Framework of Generic Repository Pattern ),希望大家耐心閱讀。
1、實(shí)體框架的初步印象
最簡(jiǎn)單的實(shí)體框架,你可以在Winform或者Web項(xiàng)目里面添加一個(gè)【ADO.NET實(shí)體數(shù)據(jù)模型】項(xiàng)開始,一步步創(chuàng)建一個(gè)基于SqlServer的實(shí)體框架項(xiàng)目。最開始,我們可以不考慮什么設(shè)計(jì)模式,能夠使用即可,因此我們可能創(chuàng)建一個(gè)比較簡(jiǎn)單的項(xiàng)目代碼,這個(gè)有助于我們了解實(shí)體框架的一些基礎(chǔ)工作原理。

為這個(gè)項(xiàng)目選定數(shù)據(jù)連接以及供測(cè)試使用的一兩個(gè)表的對(duì)象,然后完成創(chuàng)建工作,這個(gè)【ADO.NET實(shí)體數(shù)據(jù)模型】創(chuàng)建完成后,我們可以看到項(xiàng)目里面添加了一個(gè)Model1.edmx的文件,并且同時(shí)生成了幾個(gè)項(xiàng)目文件,其中包括了數(shù)據(jù)訪問對(duì)象SqlserverContext和幾個(gè)實(shí)體類(默認(rèn)為表名稱),我們也可以打開edmx的文件進(jìn)行實(shí)體類屬性的修改,如下所示。

默認(rèn)生成后,我們就可以使用這個(gè)數(shù)據(jù)訪問上下文對(duì)象SqlserverContext, 來進(jìn)行相關(guān)的數(shù)據(jù)處理操作了,簡(jiǎn)單的測(cè)試代碼如下所示。
private void GetIntData()
{
//創(chuàng)建數(shù)據(jù)訪問對(duì)象
var context = new SqlserverContext();
//新建一個(gè)實(shí)體類并賦值
TB_Province info = new TB_Province();
info.ID = 100001;
info.ProvinceName = "測(cè)試省份";
context.TB_Province.Add(info);
context.SaveChanges();
//根據(jù)主鍵判斷記錄是否存在
TB_Province info2 = context.TB_Province.Find(info.ID);
if (info2 != null)
{
Console.WriteLine("記錄已存在!");
//如果存在對(duì)象,先刪除
context.TB_Province.Remove(info2);
context.SaveChanges();
//檢查是否刪除對(duì)象
info2 = context.TB_Province.Find(info.ID);
if (info2 == null)
{
Console.WriteLine("記錄已刪除!");
}
}
//把記錄全部獲取并綁定到列表上。
var list = context.TB_Province.ToList();
this.dataGridView1.DataSource = list;
}
最后獲得的界面效果就是能夠順利執(zhí)行各種操作后把記錄顯示出來到列表上了。

2、實(shí)體框架的工作原理
1)數(shù)據(jù)訪問上下文對(duì)象介紹
從上面的代碼我們可以看到,數(shù)據(jù)訪問上下文對(duì)象SqlserverContext已經(jīng)可以直接和數(shù)據(jù)庫(kù)交互了,能夠?qū)崿F(xiàn)表對(duì)象基本增刪改查的操作功能了,那么這個(gè)類是如何的呢?為什么具有這個(gè)功能呢?
我們先看看它的代碼,SqlserverContext的類代碼如下所示(代碼為自動(dòng)生成的)。
public partial class SqlserverContext : DbContext
{
public SqlserverContext()
: base("name=SqlserverContext")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public virtual DbSet<TB_City> TB_City { get; set; }
public virtual DbSet<TB_Province> TB_Province { get; set; }
public virtual DbSet<TB_DictType> TB_DictType { get; set; }
}
其中代碼DbSet<TB_Province> TB_Province代表一個(gè)具體的數(shù)據(jù)訪問對(duì)象,對(duì)表TB_Province的數(shù)據(jù)訪問,其他的類似。我們查看.NET的內(nèi)置對(duì)象DbSet的已經(jīng)支持了一些常規(guī)的操作了。

而EMDX文件的本身是一個(gè)XML文件,它的內(nèi)容如下所示。

實(shí)體框架本身通過XML映射的方式(ORM方式),封裝了從數(shù)據(jù)庫(kù)到實(shí)體類,以及實(shí)體類到數(shù)據(jù)庫(kù)的交互過程,具體的過程我們可以參考下面的實(shí)體數(shù)據(jù)模型 (EDM)介紹。
2)實(shí)體數(shù)據(jù)模型 (EDM)介紹
Entity Framework 實(shí)體框架的要點(diǎn)是實(shí)體數(shù)據(jù)模型 (EDM),一個(gè)用于描述應(yīng)用程序域?qū)ο蟮母拍钅P汀?Entity Framework 實(shí)體框架讓開發(fā)人員可以針對(duì)實(shí)體數(shù)據(jù)模型提出查詢,而不必操心數(shù)據(jù)庫(kù)的具體操作。 實(shí)體數(shù)據(jù)模型的實(shí)體以及實(shí)體之間的關(guān)系以 XML 形式定義,而開發(fā)人員基于該模型的實(shí)體來處理強(qiáng)類型化類。
在運(yùn)行時(shí),利用特定于數(shù)據(jù)庫(kù)的 ADO.NET 提供程序,Entity Framework 實(shí)體框架將針對(duì)實(shí)體數(shù)據(jù)模型而創(chuàng)建的查詢轉(zhuǎn)換為存儲(chǔ)查詢(例如 T-SQL),然后送至數(shù)據(jù)庫(kù)。 Entity Framework 將查詢結(jié)果轉(zhuǎn)換為由強(qiáng)類型化實(shí)體類所定義的對(duì)象。

實(shí)體數(shù)據(jù)模型 (EDM),由三個(gè)概念組成。概念模型由概念架構(gòu)定義語言文件 (.csdl)來定義,映射由映射規(guī)范語言文件 (.msl),存儲(chǔ)模型(又稱邏輯模型)由存儲(chǔ)架構(gòu)定義語言文件 (.ssdl)來定義。這三者合在一起就是EDM模式。EDM模式在項(xiàng)目中的表現(xiàn)形式就是擴(kuò)展名為.edmx的文件。這個(gè)包含EDM的文件可以使用Visual Studio中的EDM設(shè)計(jì)器來設(shè)計(jì)。由于這個(gè)文件本質(zhì)是一個(gè)xml文件,可以手工編輯此文件來自定義CSDL、MSL與SSDL這三部分。

CSDL定義了EDM或者說是整個(gè)程序的靈魂部分 – 概念模型。這個(gè)文件完全以程序語言的角度來定義模型的概念。即其中定義的實(shí)體、主鍵、屬性、關(guān)聯(lián)等都是對(duì)應(yīng)于.NET Framework中的類型。
SSDL這個(gè)文件中描述了表、列、關(guān)系、主鍵及索引等數(shù)據(jù)庫(kù)中存在的概念。
MSL這個(gè)文件即上面所述的CSDL與SSDL的對(duì)應(yīng),主要包括CSDL中屬性與SSDL中列的對(duì)應(yīng)。
通過以上三個(gè)XML文件的映射關(guān)系,在程序里面,就主要是利用強(qiáng)類型數(shù)據(jù)的實(shí)體類進(jìn)行處理了,而對(duì)實(shí)體類的任何處理修改,最終會(huì)解析后得到相應(yīng)的數(shù)據(jù)庫(kù)執(zhí)行語句,然后進(jìn)行提交處理了。
3、基于泛型的倉(cāng)儲(chǔ)模式實(shí)體框架
如果基于第一點(diǎn)來構(gòu)建框架,雖然很快速,但是這樣的做法在中大型的項(xiàng)目里肯定不可取,因?yàn)樯珊蟮拇a還需要進(jìn)行多個(gè)步驟的修改調(diào)整,而且也沒有很好實(shí)現(xiàn)重用的目的,很多地方需要自己手動(dòng)編碼處理,結(jié)構(gòu)也不是很清晰,因此需要對(duì)框架進(jìn)行一步步的優(yōu)化和提煉。
在介紹基于泛型的倉(cāng)儲(chǔ)模式實(shí)體框架(The Entity Framework of Generic Repository Pattern )前,我們先來回顧一下我之前的Winform開發(fā)框架分層結(jié)構(gòu),這個(gè)基于Enterprise Library的框架,常見的分層模式,可以分為UI層、BLL層、DAL層、IDAL層、Entity層、公用類庫(kù)層等等。

這種分層可以在數(shù)據(jù)庫(kù)設(shè)計(jì)完成后,可以通過代碼生成工具,獲取到表對(duì)象的信息和關(guān)系,直接快速生成相應(yīng)的分層代碼,從而實(shí)現(xiàn)架構(gòu)、分層、命名規(guī)則等方面的一致化,并且是快速開發(fā)。
而且這種分層模式也是一種比較通用的分層結(jié)構(gòu)了,那么我們要介紹的實(shí)體框架是否也可以依照這種方式來構(gòu)建呢?是否可以結(jié)合代碼生成工具的生成模板來進(jìn)行整體性框架的開發(fā)呢?
下面我們來介紹一下泛型的倉(cāng)儲(chǔ)模式框架的具體實(shí)現(xiàn)過程。
1)實(shí)體類的代碼如下所示(先按表名生成)。
public partial class TB_City
{
public long ID { get; set; }
public string CityName { get; set; }
public string ZipCode { get; set; }
public Nullable<long> ProvinceID { get; set; }
}
2)數(shù)據(jù)訪問基類接口層(定義了幾個(gè)測(cè)試的基類接口)
/// <summary>
/// 數(shù)據(jù)訪問層基類接口
/// </summary>
/// <typeparam name="T">實(shí)體對(duì)象類型</typeparam>
public interface IBaseDAL<T> where T : class
{
T Get(object id);
IList<T> GetAll(Expression<Func<T, bool>> whereCondition);
IList<T> GetAll();
}
3)數(shù)據(jù)訪問層基類實(shí)現(xiàn)層
/// <summary>
/// 數(shù)據(jù)訪問層基類實(shí)現(xiàn)層
/// </summary>
/// <typeparam name="T">實(shí)體對(duì)象類型</typeparam>
public abstract class BaseDAL<T> : IBaseDAL<T> where T : class
{
protected DbContext baseContext;
protected IDbSet<T> objectSet;
public BaseDAL(DbContext context)
{
this.baseContext = context;
this.objectSet = this.baseContext.Set<T>();
}
public T Get(object id)
{
return objectSet.Find(id);
}
public IList<T> GetAll()
{
return objectSet.ToList<T>();;
}
public IList<T> GetAll(Expression<Func<T, bool>> whereCondition)
{
return objectSet.Where(whereCondition).ToList<T>();
}
}
4)具體數(shù)據(jù)訪問對(duì)象接口定義(城市表為例)
/// <summary>
/// 城市數(shù)據(jù)訪問層接口
/// </summary>
public interface ICityDAL : IBaseDAL<City>
{
}
5)具體數(shù)據(jù)訪問對(duì)象實(shí)現(xiàn)層(城市表為例)
/// <summary>
/// 城市數(shù)據(jù)訪問對(duì)象
/// </summary>
public class CityDAL : BaseDAL<TB_City>
{
protected MyDataContext context;
/// <summary>
/// 構(gòu)造函數(shù)
/// </summary>
/// <param name="context"></param>
public CityDAL(MyDataContext context) :base(context)
{
this.context = context;
}
6)數(shù)據(jù)倉(cāng)儲(chǔ)對(duì)象(上下文對(duì)象)
public class MyDataContext : DbContext
{
public MyDataContext() : base("name=sqlserver")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public virtual DbSet<TB_City> City { get; set; }
}
BLL、IBLL的分層和數(shù)據(jù)訪問層的類似,主要是提高一份,方便做業(yè)務(wù)整合實(shí)現(xiàn)而已,在此不再贅述。
最終實(shí)現(xiàn)倉(cāng)儲(chǔ)模式框架的分層結(jié)構(gòu)如下所示。

以上就是我對(duì)基于泛型的倉(cāng)儲(chǔ)模式的實(shí)體框架的一個(gè)初探性的開端,下面會(huì)在這個(gè)系列里面繼續(xù)分析其中存在的問題,并繼續(xù)優(yōu)化改良這個(gè)基于泛型的倉(cāng)儲(chǔ)模式的實(shí)體框架。希望大家喜歡并繼續(xù)支持。