Entity Framework Entity Framework的形成之旅--基於泛型的倉儲模式的Entity Framework(1),entityframework
很久沒有寫部落格了,一些讀者也經常問問一些問題,不過最近我確實也很忙,除了處理日常工作外,平常主要的時間也花在了繼續研究微軟的Entity Framework(EntityFramework)方面了。這個Entity Framework加入了很多特性(例如LINQ等),目前也已經應用的比較成熟了,之所以一直沒有整理成一個符合自己開發模式的Entity Framework,是因為這個架構和原來我的基於EnterpriseLibrary的模式還是有很大的不同,不過Entity Framework推出來也很久了,目前也去到了EntityFramework6了,聽說7也快出來了。
隨著我自己參考閱讀了大量的項目源碼以及對Entity Framework各個技術點的學習深入,對其中很多的方面都有自己的一些見解和心得,希望通過這個系列,能夠和讀者一步步分析,一步步深入學習這個微軟目前最為流行的.NET開發架構。本篇主要從基礎開始一步步介紹基於泛型的倉儲模式Entity Framework(The Entity Framework of Generic Repository Pattern ),希望大家耐心閱讀。
1、Entity Framework的初步印象
最簡單的Entity Framework,你可以在Winform或者Web項目裡面添加一個【ADO.NET實體資料模型】項開始,一步步建立一個基於SqlServer的Entity Framework項目。最開始,我們可以不考慮什麼設計模式,能夠使用即可,因此我們可能建立一個比較簡單的項目代碼,這個有助於我們瞭解Entity Framework的一些基礎工作原理。
為這個項目選定資料連線以及供測試使用的一兩個表的對象,然後完成建立工作,這個【ADO.NET實體資料模型】建立完成後,我們可以看到項目裡面添加了一個Model1.edmx的檔案,並且同時產生了幾個專案檔,其中包括了Data Access ObjectsSqlserverContext和幾個實體類(預設為表名稱),我們也可以開啟edmx的檔案進行實體類屬性的修改,如下所示。
預設產生後,我們就可以使用這個資料訪問內容物件SqlserverContext, 來進行相關的資料處理操作了,簡單的測試代碼如下所示。
private void GetIntData() { //建立Data Access Objects 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、Entity Framework的工作原理
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代表一個具體的Data Access Objects,對錶TB_Province的資料訪問,其他的類似。我們查看.NET的內建對象DbSet的已經支援了一些常規的操作了。
而EMDX檔案的本身是一個XML檔案,它的內容如下所示。
Entity Framework本身通過XML映射的方式(ORM方式),封裝了從資料庫到實體類,以及實體類到資料庫的互動過程,具體的過程我們可以參考下面的實體資料模型 (EDM)介紹。
2)實體資料模型 (EDM)介紹
Entity Framework Entity Framework的要點是實體資料模型 (EDM),一個用於描述應用程式定義域對象的概念性模型。 Entity Framework Entity Framework讓開發人員可以針對實體資料模型提出查詢,而不必操心資料庫的具體操作。 實體資料模型的實體以及實體之間的關係以 XML 形式定義,而開發人員基於該模型的實體來處理強型別化類。
在運行時,利用特定於資料庫的 ADO.NET 提供者,Entity Framework 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、基於泛型的倉儲模式Entity Framework
如果基於第一點來構建架構,雖然很快速,但是這樣的做法在中大型的項目裡肯定不可取,因為產生後的代碼還需要進行多個步驟的修改調整,而且也沒有很好實現重用的目的,很多地方需要自己手動編碼處理,結構也不是很清晰,因此需要對架構進行一步步的最佳化和提煉。
在介紹基於泛型的倉儲模式Entity Framework(The Entity Framework of Generic Repository Pattern )前,我們先來回顧一下我之前的Winform開發架構分層結構,這個基於Enterprise Library的架構,常見的分層模式,可以分為UI層、BLL層、DAL層、IDAL層、Entity層、公用類庫層等等。
這種分層可以在資料庫設計完成後,可以通過代碼產生工具,擷取到表對象的資訊和關係,直接快速產生相應的分層代碼,從而實現架構、分層、命名規則等方面的一致化,並且是快速開發。
而且這種分層模式也是一種比較通用的分層結構了,那麼我們要介紹的Entity Framework是否也可以依照這種方式來構建呢?是否可以結合代碼產生工具的產生模板來進行整體性架構的開發呢?
下面我們來介紹一下泛型的倉儲模式架構的具體實現過程。
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)具體Data Access Objects介面定義(城市表為例)
/// <summary> /// 城市資料訪問層介面 /// </summary> public interface ICityDAL : IBaseDAL<City> { }
5)具體Data Access Objects實現層(城市表為例)
/// <summary> /// 城市Data Access Objects /// </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的一個初探性的開端,下面會在這個系列裡面繼續分析其中存在的問題,並繼續最佳化改良這個基於泛型的倉儲模式的Entity Framework。希望大家喜歡並繼續支援。
這個系列文章索引請大家關注我的部落格,馬上整理髮布!
本文章知識由 擬人句 www.zaojuzi.com 整理髮布