企業級應用架構(二)三層架構之數據訪問層的封裝與抽象


  接上一篇我們來對數據訪問層進行封裝與抽象。在上一篇我們知道,要解除BLL對DAL的依賴,我們就必須抽象出DAL層的接口,同時基於DAL的數據訪問技術很多,如EF,ADO.NET,LINQ TO SQL,因此,我們的數據訪問層必須對這些技術提供相應的支持。所以今天我們要做的事情有兩件,第一,定義我們的數據訪問層接口;第二,屏蔽各類數據庫訪問技術的差異,提供統一的數據庫訪問模型。舉個例子,我們只需要修改一下我們的配置文件,就能夠把ADO.NET的實現方式,改變成EF的實現方式。好下面搭建我們的三層構,如圖:

      項目的框架與上一篇基本一致。項目的引用關系: StructUI->Common,Model,BLL; BLL -> Model,IDAL,Common,Factory;DAL->  IDAL,Model。再次提醒各位,我們在BLL層並沒有引用DAL,我們不能創建(new)DAL層的任何實體。

      下面,我們來定義DAL層的接口。定義層次的接口其實是一件很復雜的事情,首先,我們必須抽象出該層內所有對象有的屬性與行為。對於數據訪問層的對象,很明顯增刪改查是肯定走不掉的。其次我們必須充分的考慮,數據訪問層各類技術對該接口的實現難度,技術上沒法實現的接口,肯定是沒有任何意義的。本文僅當示范作用,所以暫時我定義的接口會很簡單,在后續的章節,我會抽象出一套完備的數據接口。該層的任何實體,都具備增刪改查功能,所以我們首先定義該層的全局接口IDALBase<T>,即任何數據訪問實體,都應該實現的接口,代碼如下

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace IDAL
{
    public interface IDALBase<T> where T : class
    {
        /// <summary>
        /// 向T對應的數據表插入
        /// 一條數據
        /// </summary>
        /// <param name="entity"></param>
        /// <returns>true:成功,false:失敗</returns>
        bool Insert(T entity);
        /// <summary>
        /// 修改數據表中與entity
        /// 實體對應的記錄
        /// </summary>
        /// <param name="entity"></param>
        /// <returns>true:成功,false:失敗</returns>
        bool Update(T entity);
        /// <summary>
        /// 刪除數據表中與entity
        /// 實體對應的記錄
        /// </summary>
        /// <param name="entity"></param>
        /// <returns>true:成功,false:失敗</returns>
        bool Delete(T entity);
        /// <summary>
        /// 查詢數據庫表中指定的ID
        /// 的記錄
        /// </summary>
        /// <param name="ID">標識ID</param>
        /// <returns>指定ID記錄對應的實體</returns>
        T Query(int ID);
    }
}
View Code

     因為,我們的接口是面向數據訪問層的所有對象,所以我們采用了范型。接着我們思考一下,除了我們全局接口所定義的方法,我們有些數據訪問類可能還有一些自己特有的公共方法,這些方法也必須用接口的形式暴露出來,因為我們BLL層並沒有引用DAL層,所以它是個只認接口的家伙,接口沒有定義的方法,它就不會去調用,綜上我們必須為每個DAL數據訪問實體提供相應的接口,並且該接口里面必須涵蓋數據訪問類型中的所有方法,例如我定義了一個IOrder接口,如下

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Model;

namespace IDAL
{
    public interface IOrderDAL : IDALBase<Order>
    {
    }
}
View Code

    它僅僅只是繼承了全局接口,假設我們的數據訪問類型Order有一個全局接口沒有定義的方法如GetAllOrder(),那么在IOrder接口中必須定義出GetAllOrder()。

    至此,我們的接口已經定義完成了,下面我們就要考慮封裝我們的DAL了。很明顯,所有的數據訪問類型都繼承了IDALBase<T>接口,那么有一點可以肯定,就是所有的數據訪問類型都必須提供IDALBase<T>的實現代碼,這個時候我們就必須設想一下,我們能否抽象出一個公共的基類,來幫我們實現IDALBase<T>接口呢?如果能的話,將極大的降低我們的代碼冗余,使我們的程序變的優雅,下面我提供一個基類DALBase<T>

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using IDAL;

namespace DAL
{
    public class DALBase<T>:IDALBase<T> where T:class
    {
        protected IDALBase<T> dalActive;

        public bool Insert(T entity)
        {
            return dalActive.Insert(entity);
        }

        public bool Update(T entity)
        {
            return dalActive.Update(entity);
        }

        public bool Delete(T entity)
        {
            return dalActive.Delete(entity);
        }

        public T Query(int ID)
        {
            return dalActive.Query(ID);
        }
    }
}
View Code

    從上面可以看到,我並沒有直接在基類里面手寫實現接口的代碼,而是調用了基類中另一個IDALBase<T>類型的實體(dalActive)來幫助我們實現實現接口。這是為什么呢? 在前面我們說過,我們要讓我們的DAL層支持各類的數據訪問技術。大家想想,如果我們在基類里面,采用了一種技術如ADO.NET去把接口給實現了,那么很顯然,我們這個DAL將不再支持任何其他的數據訪問技術,因為我數據訪問層的所有類型都繼承了這個基類,基類的數據訪問技術定型了,子類將根本沒的選。所以我們在這里定義了一個IDALBase<T>類型的屬性,讓它來幫我們實現我們的接口。

    在DAL層我提供了兩個實現類,一個是ADOBase<T>,采用了ADO.NET技術,該類不僅僅提供給接口的實現,還提供ADO.NET操作數據庫的基本API。由於該系列是我前不久才開始寫的,沒有現成的Doom,所以暫時只能拿出一個閹割版本了,但是該類要實現一些什么樣的功能,我已經用注釋說明了,在下一篇,我會提供它的具體實現。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using IDAL;

namespace DAL
{
    public class ADOBase<T>: IDALBase<T> where T : class,new()
    {
        #region ADO.net操作數據庫的基本API
        //在這一部分需要提供各類操作數據的API,作用主要有兩個:
        //第一,調用這些API實現IDALBase<T>接口
        //第二,對於所有繼承自DALBase<T>的數據訪問層實體,
        //他會取得該dalActive實體,讓后強行轉換為ADOBase<T>對象
        //調用此類API,實現其特有的功能
        #endregion

        #region 實現接口
        public virtual bool Insert(T entity)
        {
            //采用ADO實現
            return true;
        }

        public virtual bool Update(T entity)
        {
            //采用ADO實現
            return true;
        }

        public virtual bool Delete(T entity)
        {
            //采用ADO實現
            return true;
        }

        public virtual T Query(int ID)
        {
            //采用ADO實現
            return new T();
        }
        #endregion
    }
}
View Code

   另一個則是EFBase<T>,采用是EF技術,並提供EF操作數據庫的API。供DALBase<T>子類去實現其專有的方法。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using IDAL;

namespace DAL
{
    public class EFBase<T> : IDALBase<T> where T : class,new()
    {
        #region  EF操作數據庫的API
        //這里面需要提供EF操作數據庫的各類方法
        //函數
        #endregion

        #region 調用EF操作數據庫的API實現接口
        public virtual bool Insert(T entity)
        {
            //采用EF
            return true;
        }

        public virtual bool Update(T entity)
        {
            //采用EF
            return true;
        }

        public virtual bool Delete(T entity)
        {
            //采用EF
            return true;
        }

        public virtual T Query(int ID)
        {
            //采用EF
            return new T();
        }
        #endregion
    }
}
View Code

        好了現在基類我們有了,那么我們的子類則只需要在繼承基類的同時,實例化dalActive屬性,就自動的實現了IDALBase<T>接口,同時根據dalActive的類型的不同,我們的子類還具備不同的數據庫訪問方式(ADO.NET,EF),這是因為我們子類可以從父類的dalActive屬性實體拿到其對應數據訪問技術的API,我們只需強行轉換一下dalActive。例如:將dalActive強轉為EFBase,就可以得到EF的數據訪問API(當然dalActive必須是EFBase類型)。

    為了使我們的程序變的靈活,支持各類數據訪問技術,我們利DalActiveProvider的靜態方法來實例化我們基類中的dalActive屬性,該方法通過讀取配置文件來判斷,給dalActive創建何種類型的實例,本文你只提供了兩種實例(EFBase<T>與ADOBase<T>)所以相對簡單,如下

using IDAL;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Text;

namespace DAL
{
    public class DalActiveProvider<T> where T:class,new()
    {
        private static string className = ConfigurationManager.AppSettings["dalActiveName"];
        public static IDALBase<T> GetDalActive()
        {
            if (string.Equals(className, "EFBase"))
            {
                return new EFBase<T>();
            }
            else
            {
                return new ADOBase<T>();
            }
        }
    }
}
View Code

  下面我定義了一個OrderDAL數據訪類

using Common;
using Factory;
using IDAL;
using Model;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DAL
{
    public class OrderDAL : DALBase<Order>, IOrderDAL
    {
        public OrderDAL()
        {
            this.dalActive = DalActiveProvider<Order>.GetDalActive();
        }

        #region 專有公共方法
        //根據技術的選型
        //將dalActive強行轉換
        //為ADOBase<T>或者EFBase,在調用其
        //數據訪問API來實現專用公共方法
        #endregion
    }
}
View Code

  我們的配置文件,如圖
    

   很顯然,目前我們會創建EFBase<T>實例給基類的dalActive屬性,整個DAL層將采用EF數據訪問技術,如果我們想采用ADO.NET技術的話,我們只需要將配置文件的dalActiveName的Value改為與字符創“EFBase”不等的任何字符串即可。

 至於BLL層如何調用DAL層,在我的上一篇文章已經做了詳細的介紹,所以我不多說了,工廠代碼如下

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace Factory
{
    public class InstancesFactory
    {
        /// <summary>
        /// 創建指定類型T的實例
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="assemblyPath">程序集路徑</param>
        /// <param name="className">類名稱(非全名)</param>
        /// <returns>T類型實例</returns>
        public static T CreateOrder<T>(string assemblyPath,string className)
        {
            className = string.Format("{0}.{1}", assemblyPath, className);
            return (T)Assembly.Load(assemblyPath).CreateInstance(className);
        }
    }
}
View Code

  我在BLL層創建了一個很簡單的基類BLLBase<T>,里面只有一個實現IDALBase<T>接口的屬性dalService,如下

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using IDAL;

namespace BLL
{
    public class BLLBase<T> where T:class
    {
        /// <summary>
        /// 數據庫接口
        /// </summary>
        protected  IDALBase<T> dalService;
    }
}
View Code

  在BLL的我創建一個業務子類OrderBLL繼承BLLBase<T>,在構造函數中采用工廠實例化dalService,然后OrderBLL針對數據庫的所有操作都是調用dalService的對應方法。當然,如果OrderBLL需要調用一些OrderDAL的專有方法,我還必須把dalService強行轉換為IOrderDAL接口類型

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Model;
using Factory;
using IDAL;
using Common;

namespace BLL
{
    public class OrderBLL:BLLBase<Order>
    {
        public OrderBLL()
        {
            //初始化dal服務
            this.dalService = InstancesFactory.CreateOrder<IOrderDAL>
                (FactoryConfig.Order.AssemblyPath, FactoryConfig.Order.ClassName);
        }
        public bool Inset(Order order)
        {
            //業務1
            //業務2
            return dalService.Insert(order);
        }

        #region IOrderDAL的專用方法
        public void 專用方法Dom()
        {
            IOrderDAL orderbll = dalService as IOrderDAL;
            //調用IOrderDAL的專有方法
            //orderbll.專有方法();
        }
        #endregion
    }
}
View Code
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Text;

namespace Common
{
    public class ConfigEntity
    {
        /// <summary>
        /// 程序路徑
        /// </summary>
        public string AssemblyPath { get; set; }
        /// <summary>
        /// 類命
        /// </summary>
        public string ClassName { get; set; }
    }

    public class FactoryConfig
    {
        public static ConfigEntity Order
        {
            get 
            {
                return new ConfigEntity()
                {
                    AssemblyPath=ConfigurationManager.AppSettings["AssemblyPath"],
                    ClassName = ConfigurationManager.AppSettings["ClassName"]
                };
            }
        }
    }
}
View Code

總結  

   本文對數據訪問層進行了概要上的封裝與抽象。當然在定義接口方面我們做的很簡單,在后面的章節我會完善並抽象出一個相對完備的接口,另外對於DAL的封裝我們還有兩大核心類(EFBase<tT>與ADOBase<T>)沒有去實現,對於這兩個類的實現將會是我隨后的幾篇博文的核心課題,最后感謝大家的觀看

本文的DOME 在這里

 

 


免責聲明!

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



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