Entity Framework 實體框架的形成之旅--基類接口的統一和異步操作的實現(3)


在本系列的第一篇隨筆《Entity Framework 實體框架的形成之旅--基於泛型的倉儲模式的實體框架(1)》中介紹了Entity Framework 實體框架的一些基礎知識,以及構建了一個簡單的基於泛型的倉儲模式的框架;在隨筆《Entity Framework 實體框架的形成之旅--利用Unity對象依賴注入優化實體框架(2)》則持續優化這個倉儲模式的實體框架,主要介紹業務邏輯層的構建,以及利用Unity和反射進行動態的對象注冊。本篇主要介紹基類接口的統一和異步操作的實現等方面,逐步把我框架接口命名的方式進行統一,並增加所有必要用到的增刪改查、分頁、lambda表達式條件處理,以及異步操作等特性,這樣能夠盡可能的符合基類這個特殊類的定義,實現功能接口的最大化重用和統一。

1、基類接口的統一命名和擴展

在我以前的基於Enterprise Library的框架里面,定義了一個超級的數據訪問基類,是特定數據訪問類基類的基類,AbstractBaseDAL的數據訪問層基類定義了很多通用的接口,具有非常強大的操作功能,如下所示。

這里面的很多接口命名我都經過了一些推敲,或者我基於我或者我客戶群體的使用習慣和理解考慮,也是想沿承這些命名規則,擴充我這個基於泛型的倉儲模式的實體框架基類接口。

下面是各類不同接口的定義內容。

1)增加操作

        /// <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)修改操作

        /// <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>兩種方式)

        /// <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)分頁查詢操作

        /// <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;

 

2、異步操作的定義和調用

在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)

Entity Framework 實體框架的形成之旅--利用Unity對象依賴注入優化實體框架(2) 

Entity Framework 實體框架的形成之旅--基類接口的統一和異步操作的實現(3)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM