在本系列的第一篇隨筆《Entity Framework 實體框架的造成之旅--基於泛型的倉儲模式的實體框架(1)》中介紹了Entity Framework 實體框架的一些基礎知識,以及構建了一個簡單的基於泛型的倉儲模式的框架;在隨筆《Entity Framework 實體框架的造成之旅--利用Unity對象依賴注入優化實體框架(2)》則持續優化這個倉儲模式的實體框架,主要介紹業務邏輯層的構建,以及利用Unity和反射進行動態的對象註冊。本篇主要介紹基類接口的統一和異步操做的實現等方面,逐步把我框架接口命名的方式進行統一,並增長全部必要用到的增刪改查、分頁、lambda表達式條件處理,以及異步操做等特性,這樣可以儘量的符合基類這個特殊類的定義,實現功能接口的最大化重用和統一。html
在我之前的基於Enterprise Library的框架裏面,定義了一個超級的數據訪問基類,是特定數據訪問類基類的基類,AbstractBaseDAL的數據訪問層基類定義了不少通用的接口,具備很是強大的操做功能,以下所示。數據庫
這裏面的不少接口命名我都通過了一些推敲,或者我基於我或者我客戶羣體的使用習慣和理解考慮,也是想沿承這些命名規則,擴充我這個基於泛型的倉儲模式的實體框架基類接口。框架
下面是各種不一樣接口的定義內容。異步
1)增長操做async
/// <summary> /// 插入指定對象到數據庫中 /// </summary> /// <param name="t">指定的對象</param> /// <returns>執行成功返回<c>true</c>,不然爲<c>false</c></returns> bool Insert(T t);
2)刪除操做函數
/// <summary> /// 根據指定對象的ID,從數據庫中刪除指定對象 /// </summary> /// <param name="id">對象的ID</param> /// <returns>執行成功返回<c>true</c>,不然爲<c>false</c>。</returns> bool Delete(object id); /// <summary> /// 從數據庫中刪除指定對象 /// </summary> /// <param name="id">指定對象</param> /// <returns>執行成功返回<c>true</c>,不然爲<c>false</c>。</returns> bool Delete(T t);
3)修改操做post
/// <summary> /// 更新對象屬性到數據庫中 /// </summary> /// <param name="t">指定的對象</param> /// <param name="key">主鍵的值</param> /// <returns>執行成功返回<c>true</c>,不然爲<c>false</c></returns> bool Update(T t, object key);
4)主鍵查詢以及條件查詢操做優化
/// <summary> /// 查詢數據庫,返回指定ID的對象 /// </summary> /// <param name="id">ID主鍵的值</param> /// <returns>存在則返回指定的對象,不然返回Null</returns> T FindByID(object id); /// <summary> /// 根據條件查詢數據庫,若是存在返回第一個對象 /// </summary> /// <param name="match">條件表達式</param> /// <returns>存在則返回指定的第一個對象,不然返回默認值</returns> T FindSingle(Expression<Func<T, bool>> match);
5)集合查詢(分返回IQueryable和ICollection<T>兩種方式)this
/// <summary> /// 返回可查詢的記錄源 /// </summary> /// <returns></returns> IQueryable<T> GetQueryable(); /// <summary> /// 根據條件表達式返回可查詢的記錄源 /// </summary> /// <param name="match">查詢條件</param> /// <param name="orderByProperty">排序表達式</param> /// <param name="isDescending">若是爲true則爲降序,不然爲升序</param> /// <returns></returns> IQueryable<T> GetQueryable(Expression<Func<T, bool>> match, string sortPropertyName, bool isDescending = true); /// <summary> /// 根據條件查詢數據庫,並返回對象集合 /// </summary> /// <param name="match">條件表達式</param> /// <returns></returns> ICollection<T> Find(Expression<Func<T, bool>> match); /// <summary> /// 根據條件查詢數據庫,並返回對象集合 /// </summary> /// <param name="match">條件表達式</param> /// <param name="orderByProperty">排序表達式</param> /// <param name="isDescending">若是爲true則爲降序,不然爲升序</param> /// <returns></returns> ICollection<T> Find<TKey>(Expression<Func<T, bool>> match, Expression<Func<T, TKey>> orderByProperty, bool isDescending = true);
6)分頁查詢操做url
/// <summary> /// 根據條件查詢數據庫,並返回對象集合(用於分頁數據顯示) /// </summary> /// <param name="match">條件表達式</param> /// <param name="info">分頁實體</param> /// <param name="orderByProperty">排序表達式</param> /// <param name="isDescending">若是爲true則爲降序,不然爲升序</param> /// <returns>指定對象的集合</returns> ICollection<T> FindWithPager(Expression<Func<T, bool>> match, PagerInfo info); /// <summary> /// 根據條件查詢數據庫,並返回對象集合(用於分頁數據顯示) /// </summary> /// <param name="match">條件表達式</param> /// <param name="info">分頁實體</param> /// <param name="orderByProperty">排序表達式</param> /// <param name="isDescending">若是爲true則爲降序,不然爲升序</param> /// <returns>指定對象的集合</returns> ICollection<T> FindWithPager<TKey>(Expression<Func<T, bool>> match, PagerInfo info, Expression<Func<T, TKey>> orderByProperty, bool isDescending = true);
這樣咱們在BaseDAL裏面,把這些接口所有實現了,那麼全部繼承這個基類對象的數據訪問對象,就具備這些標準的接口了,也給咱們開發實現了總體性的統一。
首先咱們來看看這個基類BaseDAL的初始化定義代碼。
/// <summary> /// 數據訪問層基類 /// </summary> /// <typeparam name="T">實體對象類型</typeparam> public abstract class BaseDAL<T> : IBaseDAL<T> where T : class { #region 變量及構造函數 /// <summary> /// DbContext對象 /// </summary> protected DbContext baseContext; /// <summary> /// 指定類型的實體對象集合 /// </summary> protected DbSet<T> objectSet; /// <summary> /// 是否爲降序 /// </summary> public bool IsDescending { get; set; } /// <summary> /// 排序屬性 /// </summary> public string SortPropertyName { get; set; } /// <summary> /// 參數化構造函數 /// </summary> /// <param name="context">DbContext對象</param> public BaseDAL(DbContext context) { this.baseContext = context; this.objectSet = this.baseContext.Set<T>(); this.IsDescending = true; this.SortPropertyName = "ID"; } #endregion
有了這些DbContext對象以及DbSet<T>對象,具體的接口實現就很容易了,下面我抽幾個表明性的函數來介紹實現。
1)增長對象
/// <summary> /// 插入指定對象到數據庫中 /// </summary> /// <param name="t">指定的對象</param> /// <returns>執行成功返回<c>true</c>,不然爲<c>false</c></returns> public virtual bool Insert(T t) { ArgumentValidation.CheckForNullReference(t, "傳入的對象t爲空"); objectSet.Add(t); return baseContext.SaveChanges() > 0; }
2)刪除對象
/// <summary> /// 根據指定對象的ID,從數據庫中刪除指定對象 /// </summary> /// <param name="id">對象的ID</param> /// <returns>執行成功返回<c>true</c>,不然爲<c>false</c>。</returns> public virtual bool Delete(object id) { T obj = objectSet.Find(id); objectSet.Remove(obj); return baseContext.SaveChanges() > 0; }
3)修改對象
/// <summary> /// 更新對象屬性到數據庫中 /// </summary> /// <param name="t">指定的對象</param> /// <param name="key">主鍵的值</param> /// <returns>執行成功返回<c>true</c>,不然爲<c>false</c></returns> public virtual bool Update(T t, object key) { ArgumentValidation.CheckForNullReference(t, "傳入的對象t爲空"); bool result = false; T existing = objectSet.Find(key); if (existing != null) { baseContext.Entry(existing).CurrentValues.SetValues(t); result = baseContext.SaveChanges() > 0; } return result; }
4)根據條件查詢
/// <summary> /// 根據條件查詢數據庫,若是存在返回第一個對象 /// </summary> /// <param name="match">條件表達式</param> /// <returns>存在則返回指定的第一個對象,不然返回默認值</returns> public virtual T FindSingle(Expression<Func<T, bool>> match) { return objectSet.FirstOrDefault(match); } /// <summary> /// 根據條件表達式返回可查詢的記錄源 /// </summary> /// <param name="match">查詢條件</param> /// <param name="orderByProperty">排序表達式</param> /// <param name="isDescending">若是爲true則爲降序,不然爲升序</param> /// <returns></returns> public virtual IQueryable<T> GetQueryable(Expression<Func<T, bool>> match, string sortPropertyName, bool isDescending = true) { IQueryable<T> query = this.objectSet; if (match != null) { query = query.Where(match); } return query.OrderBy(sortPropertyName, isDescending); }
5)分頁查詢
/// <summary> /// 根據條件查詢數據庫,並返回對象集合(用於分頁數據顯示) /// </summary> /// <param name="match">條件表達式</param> /// <param name="info">分頁實體</param> /// <param name="orderByProperty">排序表達式</param> /// <param name="isDescending">若是爲true則爲降序,不然爲升序</param> /// <returns>指定對象的集合</returns> public virtual ICollection<T> FindWithPager(Expression<Func<T, bool>> match, PagerInfo info) { int pageindex = (info.CurrenetPageIndex < 1) ? 1 : info.CurrenetPageIndex; int pageSize = (info.PageSize <= 0) ? 20 : info.PageSize; int excludedRows = (pageindex - 1) * pageSize; IQueryable<T> query = GetQueryable().Where(match); info.RecordCount = query.Count(); return query.Skip(excludedRows).Take(pageSize).ToList(); }
更多的代碼就不一一貼出,反正咱們所有實現本身所需的各類操做就能夠了,這裏要提的是,咱們儘量利用Lambda表達式進行條件處理,包括查詢、刪除等條件處理。
對上面的這些常規接口,咱們調用代碼處理的例子以下所示。
private void btnProvince_Click(object sender, EventArgs e) { DateTime dt = DateTime.Now; var list = BLLFactory<ProvinceBLL>.Instance.GetAll(s=>s.ProvinceName); this.dataGridView1.DataSource = list; Console.WriteLine("花費時間:{0}", DateTime.Now.Subtract(dt).TotalMilliseconds); } private void btnCity_Click(object sender, EventArgs e) { DateTime dt = DateTime.Now; CityBLL bll = new CityBLL(); var result = bll.GetAll(); this.dataGridView1.DataSource = result; Console.WriteLine("花費時間:{0}", DateTime.Now.Subtract(dt).TotalMilliseconds); }
若是須要考慮分頁,以上接口已經定義了分頁處理的接口和實現了,咱們在業務對象裏面直接調用接口就能夠了,具體代碼以下所示。
CityBLL bll = new CityBLL(); PagerInfo info = new PagerInfo(); info.PageSize = 30; info.CurrenetPageIndex =1 ; ICollection<City> list; if (i++ % 2 == 0) { sortType = "自定義排序"; //使用自定義排序 list = bll.FindWithPager(s => s.CityName.Contains("南"), info, o => o.ID, true); } else { sortType = "默認字段排序"; //使用默認字段排序 list = bll.FindWithPager(s => s.CityName.Contains("南"), info); } this.dataGridView1.DataSource = list;
在EF裏面實現異步(並行)很是容易,在.NET 4.5裏因爲async/await關鍵字的出現,使得實現異步變得更加容易。
使用await關鍵字後,.NET會自動把返回結果包裝在一個Task類型的對象中。使用await表達式時,控制會返回到調用此方法的線程中;在await等待的方法執行完畢後,控制會自動返回到下面的語句中。發生異常時,異常會在await表達式中拋出。
咱們基本上全部的增刪改查、分頁等接口,均可以使用異步操做來定義這些新接口,代碼以下所示。
1)增長對象異步實現
異步定義的接口以下所示
/// <summary> /// 插入指定對象到數據庫中(異步) /// </summary> /// <param name="t">指定的對象</param> /// <returns>執行成功返回<c>true</c>,不然爲<c>false</c></returns> Task<bool> InsertAsync(T t);
接口的實現以下所示
/// <summary> /// 插入指定對象到數據庫中(異步) /// </summary> /// <param name="t">指定的對象</param> /// <returns>執行成功返回<c>true</c>,不然爲<c>false</c></returns> public virtual async Task<bool> InsertAsync(T t) { ArgumentValidation.CheckForNullReference(t, "傳入的對象t爲空"); objectSet.Add(t); return await baseContext.SaveChangesAsync() > 0; }
和普通的接口定義不同的地方,咱們看到異步的接口都是以Async結尾,而且返回值使用Task<T>進行包裝,另外實現裏面,增長了async的定義,方法體裏面增長 await 的關鍵字,這些就構成了異步操做的接口定義和接口實現了。
2)條件刪除異步實現
咱們再來看一個複雜一點的條件刪除操做,代碼以下所示。
定義接口
/// <summary> /// 根據指定條件,從數據庫中刪除指定對象(異步) /// </summary> /// <param name="match">條件表達式</param> /// <returns>執行成功返回<c>true</c>,不然爲<c>false</c>。</returns> Task<bool> DeleteByConditionAsync(Expression<Func<T, bool>> match);
接口實現
/// <summary> /// 根據指定條件,從數據庫中刪除指定對象(異步) /// </summary> /// <param name="match">條件表達式</param> /// <returns>執行成功返回<c>true</c>,不然爲<c>false</c>。</returns> public virtual async Task<bool> DeleteByConditionAsync(Expression<Func<T, bool>> match) { objectSet.Where<T>(match).ToList<T>().ForEach(d => baseContext.Entry<T>(d).State = EntityState.Deleted); return await baseContext.SaveChangesAsync() > 0; }
咱們定義的這些異步接口,基本上都是相似的操做,可是咱們應該如何調用異步的處理呢?
好像有兩個調用代碼方式。
1)使用async和await關鍵字處理
private async void btnCity_Click(object sender, EventArgs e) { DateTime dt = DateTime.Now; CityBLL bll = new CityBLL(); var result = await bll.GetAllAsync(); this.dataGridView1.DataSource = result; Console.WriteLine("花費時間:{0}", DateTime.Now.Subtract(dt).TotalMilliseconds); }
2)使用 await Task.Run的處理方式
private async void btnCity_Click(object sender, EventArgs e) { DateTime dt = DateTime.Now; CityBLL bll = new CityBLL(); var result = await Task.Run(() => { var list = bll.GetAllAsync(); return list; }); this.dataGridView1.DataSource = result; Console.WriteLine("花費時間:{0}", DateTime.Now.Subtract(dt).TotalMilliseconds); }
兩種方式都能正常運行,並獲得所要的效果。
本篇主要介紹了基類接口的統一封裝、並增長全部必要的增刪改查、分頁查詢、Lambda條件等處理方式,還有就是增長了相關的異步操做接口和實現,隨着咱們對通用功能的進一步要求,能夠爲基類增長更多的接口函數。
這個系列文章索引以下:
Entity Framework 實體框架的造成之旅--基於泛型的倉儲模式的實體框架(1)