很久沒有寫博客了,一些讀者也經常問問一些問題,不過最近我確實也很忙,除了處理日常工作外,平常主要的時間也花在了繼續研究微軟的實體框架(EntityFramework)方面了。這個實體框架加入了很多特性(例如LINQ等),目前也已經應用的比較成熟了,之所以一直沒有整理成一個符合自己開發模式的實體框架,是因為這個框架和原來我的基於EnterpriseLibrary的模式還是有很大的不同,不過實體框架推出來也很久了,目前也去到了EntityFramework6了,聽說7也快出來了。
隨着我自己參考閱讀了大量的項目源碼以及對實體框架各個技術點的學習深入,對其中很多的方面都有自己的一些見解和心得,希望通過這個系列,能夠和讀者一步步分析,一步步深入學習這個微軟目前最為流行的.NET開發框架。本篇主要從基礎開始一步步介紹基於泛型的倉儲模式實體框架(The Entity Framework of Generic Repository Pattern ),希望大家耐心閱讀。
1、實體框架的初步印象
最簡單的實體框架,你可以在Winform或者Web項目里面添加一個【ADO.NET實體數據模型】項開始,一步步創建一個基於SqlServer的實體框架項目。最開始,我們可以不考慮什么設計模式,能夠使用即可,因此我們可能創建一個比較簡單的項目代碼,這個有助於我們了解實體框架的一些基礎工作原理。
為這個項目選定數據連接以及供測試使用的一兩個表的對象,然后完成創建工作,這個【ADO.NET實體數據模型】創建完成后,我們可以看到項目里面添加了一個Model1.edmx的文件,並且同時生成了幾個項目文件,其中包括了數據訪問對象SqlserverContext和幾個實體類(默認為表名稱),我們也可以打開edmx的文件進行實體類屬性的修改,如下所示。
默認生成后,我們就可以使用這個數據訪問上下文對象SqlserverContext, 來進行相關的數據處理操作了,簡單的測試代碼如下所示。
private void GetIntData() { //創建數據訪問對象 var context = new SqlserverContext(); //新建一個實體類並賦值 TB_Province info = new TB_Province(); info.ID = 100001; info.ProvinceName = "測試省份"; context.TB_Province.Add(info); context.SaveChanges(); //根據主鍵判斷記錄是否存在 TB_Province info2 = context.TB_Province.Find(info.ID); if (info2 != null) { Console.WriteLine("記錄已存在!"); //如果存在對象,先刪除 context.TB_Province.Remove(info2); context.SaveChanges(); //檢查是否刪除對象 info2 = context.TB_Province.Find(info.ID); if (info2 == null) { Console.WriteLine("記錄已刪除!"); } } //把記錄全部獲取並綁定到列表上。 var list = context.TB_Province.ToList(); this.dataGridView1.DataSource = list; }
最后獲得的界面效果就是能夠順利執行各種操作后把記錄顯示出來到列表上了。
2、實體框架的工作原理
1)數據訪問上下文對象介紹
從上面的代碼我們可以看到,數據訪問上下文對象SqlserverContext已經可以直接和數據庫交互了,能夠實現表對象基本增刪改查的操作功能了,那么這個類是如何的呢?為什么具有這個功能呢?
我們先看看它的代碼,SqlserverContext的類代碼如下所示(代碼為自動生成的)。
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代表一個具體的數據訪問對象,對表TB_Province的數據訪問,其他的類似。我們查看.NET的內置對象DbSet的已經支持了一些常規的操作了。
而EMDX文件的本身是一個XML文件,它的內容如下所示。
實體框架本身通過XML映射的方式(ORM方式),封裝了從數據庫到實體類,以及實體類到數據庫的交互過程,具體的過程我們可以參考下面的實體數據模型 (EDM)介紹。
2)實體數據模型 (EDM)介紹
Entity Framework 實體框架的要點是實體數據模型 (EDM),一個用於描述應用程序域對象的概念模型。 Entity Framework 實體框架讓開發人員可以針對實體數據模型提出查詢,而不必操心數據庫的具體操作。 實體數據模型的實體以及實體之間的關系以 XML 形式定義,而開發人員基於該模型的實體來處理強類型化類。
在運行時,利用特定於數據庫的 ADO.NET 提供程序,Entity Framework 實體框架將針對實體數據模型而創建的查詢轉換為存儲查詢(例如 T-SQL),然后送至數據庫。 Entity Framework 將查詢結果轉換為由強類型化實體類所定義的對象。
實體數據模型 (EDM),由三個概念組成。概念模型由概念架構定義語言文件 (.csdl)來定義,映射由映射規范語言文件 (.msl),存儲模型(又稱邏輯模型)由存儲架構定義語言文件 (.ssdl)來定義。這三者合在一起就是EDM模式。EDM模式在項目中的表現形式就是擴展名為.edmx的文件。這個包含EDM的文件可以使用Visual Studio中的EDM設計器來設計。由於這個文件本質是一個xml文件,可以手工編輯此文件來自定義CSDL、MSL與SSDL這三部分。
CSDL定義了EDM或者說是整個程序的靈魂部分 – 概念模型。這個文件完全以程序語言的角度來定義模型的概念。即其中定義的實體、主鍵、屬性、關聯等都是對應於.NET Framework中的類型。
SSDL這個文件中描述了表、列、關系、主鍵及索引等數據庫中存在的概念。
MSL這個文件即上面所述的CSDL與SSDL的對應,主要包括CSDL中屬性與SSDL中列的對應。
通過以上三個XML文件的映射關系,在程序里面,就主要是利用強類型數據的實體類進行處理了,而對實體類的任何處理修改,最終會解析后得到相應的數據庫執行語句,然后進行提交處理了。
3、基於泛型的倉儲模式實體框架
如果基於第一點來構建框架,雖然很快速,但是這樣的做法在中大型的項目里肯定不可取,因為生成后的代碼還需要進行多個步驟的修改調整,而且也沒有很好實現重用的目的,很多地方需要自己手動編碼處理,結構也不是很清晰,因此需要對框架進行一步步的優化和提煉。
在介紹基於泛型的倉儲模式實體框架(The Entity Framework of Generic Repository Pattern )前,我們先來回顧一下我之前的Winform開發框架分層結構,這個基於Enterprise Library的框架,常見的分層模式,可以分為UI層、BLL層、DAL層、IDAL層、Entity層、公用類庫層等等。
這種分層可以在數據庫設計完成后,可以通過代碼生成工具,獲取到表對象的信息和關系,直接快速生成相應的分層代碼,從而實現架構、分層、命名規則等方面的一致化,並且是快速開發。
而且這種分層模式也是一種比較通用的分層結構了,那么我們要介紹的實體框架是否也可以依照這種方式來構建呢?是否可以結合代碼生成工具的生成模板來進行整體性框架的開發呢?
下面我們來介紹一下泛型的倉儲模式框架的具體實現過程。
1)實體類的代碼如下所示(先按表名生成)。
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)數據訪問基類接口層(定義了幾個測試的基類接口)
/// <summary> /// 數據訪問層基類接口 /// </summary> /// <typeparam name="T">實體對象類型</typeparam> public interface IBaseDAL<T> where T : class { T Get(object id); IList<T> GetAll(Expression<Func<T, bool>> whereCondition); IList<T> GetAll(); }
3)數據訪問層基類實現層
/// <summary> /// 數據訪問層基類實現層 /// </summary> /// <typeparam name="T">實體對象類型</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)具體數據訪問對象接口定義(城市表為例)
/// <summary> /// 城市數據訪問層接口 /// </summary> public interface ICityDAL : IBaseDAL<City> { }
5)具體數據訪問對象實現層(城市表為例)
/// <summary> /// 城市數據訪問對象 /// </summary> public class CityDAL : BaseDAL<TB_City> { protected MyDataContext context; /// <summary> /// 構造函數 /// </summary> /// <param name="context"></param> public CityDAL(MyDataContext context) :base(context) { this.context = context; }
6)數據倉儲對象(上下文對象)
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的分層和數據訪問層的類似,主要是提高一份,方便做業務整合實現而已,在此不再贅述。
最終實現倉儲模式框架的分層結構如下所示。
以上就是我對基於泛型的倉儲模式的實體框架的一個初探性的開端,下面會在這個系列里面繼續分析其中存在的問題,並繼續優化改良這個基於泛型的倉儲模式的實體框架。希望大家喜歡並繼續支持。
這個系列文章索引如下:
Entity Framework 實體框架的形成之旅--基於泛型的倉儲模式的實體框架(1)
Entity Framework 實體框架的形成之旅--利用Unity對象依賴注入優化實體框架(2)
Entity Framework 實體框架的形成之旅--基類接口的統一和異步操作的實現(3)