接著上一篇關(guān)于分層架構(gòu)的討論,一個(gè)分層架構(gòu)設(shè)計(jì)的例子(1)。
上篇介紹了實(shí)體類(Entity)、數(shù)據(jù)庫訪問類(DAL)、數(shù)據(jù)訪問接口(IDAL)的相關(guān)設(shè)計(jì),本篇主要討論下面幾個(gè)部分內(nèi)容:業(yè)務(wù)邏輯層、緩存機(jī)制、界面層等方面。 業(yè)務(wù)邏輯層,主要是業(yè)務(wù)邏輯基類的設(shè)計(jì),由于數(shù)據(jù)庫訪問類(DAL)的基類封裝了大量的操作實(shí)現(xiàn),因此,業(yè)務(wù)邏輯層的主要工作是進(jìn)一步封裝對(duì)底層訪問接口的實(shí)現(xiàn),如下所示。 public class BaseBLL<T> where T : BaseEntity, new() { 構(gòu)造函數(shù)#region 構(gòu)造函數(shù) private string dalName = ""; protected IBaseDAL<T> baseDal = null; public BaseBLL() : this("") { } public BaseBLL(string dalName) { this.dalName = dalName; if (string.IsNullOrEmpty(dalName)) { this.dalName = GetType().Name; } baseDal = Reflect<IBaseDAL<T>>.Create(this.dalName, "HuaweiSoftware.IPSPBD.DAL"); } #endregion 對(duì)象添加、修改、刪除等接口#region 對(duì)象添加、修改、刪除等接口 /**//// <summary> /// 插入指定對(duì)象到數(shù)據(jù)庫中 /// </summary> /// <param name="obj">指定的對(duì)象</param> /// <returns>執(zhí)行成功返回新增記錄的自增長ID。</returns> public virtual bool Insert(T obj) { return baseDal.Insert(obj); } /**//// <summary> /// 更新對(duì)象屬性到數(shù)據(jù)庫中 /// </summary> /// <param name="obj">指定的對(duì)象</param> /// <returns>執(zhí)行成功返回<c>true</c>,否則為<c>false</c>。</returns> public virtual bool Update(T obj, string primaryKeyValue) { return baseDal.Update(obj, primaryKeyValue); } /**//// <summary> /// 查詢數(shù)據(jù)庫,檢查是否存在指定ID的對(duì)象(用于字符型主鍵) /// </summary> /// <param name="key">對(duì)象的ID值</param> /// <returns>存在則返回指定的對(duì)象,否則返回Null</returns> public virtual T FindByID(string key) { return baseDal.FindByID(key); } /**//// <summary> /// 查詢數(shù)據(jù)庫,檢查是否存在指定鍵值的對(duì)象 /// </summary> /// <param name="fieldName">指定的屬性名</param> /// <param name="key">指定的值</param> /// <returns>存在則返回<c>true</c>,否則為<c>false</c>。</returns> public virtual bool IsExistKey(string fieldName, object key) { return baseDal.IsExistKey(fieldName, key); } /**//// <summary> /// 根據(jù)指定對(duì)象的ID,從數(shù)據(jù)庫中刪除指定對(duì)象(用于整型主鍵) /// </summary> /// <param name="key">指定對(duì)象的ID</param> /// <returns>執(zhí)行成功返回<c>true</c>,否則為<c>false</c>。</returns> public virtual bool Delete(string key) { return baseDal.DeleteByKey(key); } /**//// <summary> /// 根據(jù)指定條件,從數(shù)據(jù)庫中刪除指定對(duì)象 /// </summary> /// <param name="condition">刪除記錄的條件語句</param> /// <returns>執(zhí)行成功返回<c>true</c>,否則為<c>false</c>。</returns> public virtual bool DeleteByCondition(string condition) { return baseDal.DeleteByCondition(condition); } #endregion 返回集合的接口#region 返回集合的接口 /**//// <summary> /// 根據(jù)ID字符串(逗號(hào)分隔)獲取對(duì)象列表 /// </summary> /// <param name="idString">ID字符串(逗號(hào)分隔)</param> /// <returns>符合條件的對(duì)象列表</returns> public virtual List<T> FindByIDs(string idString) { return baseDal.FindByIDs(idString); } /**//// <summary> /// 根據(jù)條件查詢數(shù)據(jù)庫,并返回對(duì)象集合 /// </summary> /// <param name="condition">查詢的條件</param> /// <returns>指定對(duì)象的集合</returns> public virtual List<T> Find(string condition) { return Find(condition); } /**//// <summary> /// 根據(jù)條件查詢數(shù)據(jù)庫,并返回對(duì)象集合(用于分頁數(shù)據(jù)顯示) /// </summary> /// <param name="condition">查詢的條件</param> /// <param name="info">分頁實(shí)體</param> /// <returns>指定對(duì)象的集合</returns> public virtual List<T> Find(string condition, PagerInfo info) { return baseDal.Find(condition, info); } /**//// <summary> /// 返回?cái)?shù)據(jù)庫所有的對(duì)象集合 /// </summary> /// <returns>指定對(duì)象的集合</returns> public virtual List<T> GetAll() { return baseDal.GetAll(); } /**//// <summary> /// 返回?cái)?shù)據(jù)庫所有的對(duì)象集合(用于分頁數(shù)據(jù)顯示) /// </summary> /// <param name="info">分頁實(shí)體信息</param> /// <returns>指定對(duì)象的集合</returns> public virtual List<T> GetAll(PagerInfo info) { return baseDal.GetAll(info); } public virtual DataSet GetAllToDataSet(PagerInfo info) { return baseDal.GetAllToDataSet(info); } #endregion } 業(yè)務(wù)層基類封裝了大量的調(diào)用,那么對(duì)于業(yè)務(wù)層的具體操作類,它的工作就很簡單了,基本上只需要繼承一下基類就可以了,這就是有一個(gè)優(yōu)秀父親的好處,呵呵 public class Equipment : BaseBLL<EquipmentInfo> { public Equipment() : base() { } } 基本上,業(yè)務(wù)層的設(shè)計(jì)到此應(yīng)該收尾了,可是我們注意到,很多開發(fā)都使用了緩存的機(jī)制來進(jìn)一步提高程序的性能,下面對(duì)這方面進(jìn)行討論。緩存的機(jī)制,一般是把創(chuàng)建過的對(duì)象資源放到一個(gè)集合中,需要的時(shí)候,調(diào)出來,如下業(yè)務(wù)層的工廠類所示。 public class BLLFactory<T> where T : class { private static Hashtable objCache = new Hashtable(); public static T Instance { get { string CacheKey = typeof(T).FullName; T bll = (T)objCache[CacheKey]; //從緩存讀取 if (bll == null) { bll = Reflect<T>.Create(typeof(T).Name, "HuaweiSoftware.IPSPBD.BLL"); //反射創(chuàng)建,并緩存 } return bll; } } } 這是一個(gè)業(yè)務(wù)邏輯類工廠創(chuàng)建類,我們?cè)诮缑鎸又恍枰缦抡{(diào)用即可構(gòu)造一個(gè)(利用了緩存)具體的業(yè)務(wù)類出來 CustomerInfo info = BLLFactory<Customer>.Instance.FindByID(ID); 在上面的BaseBLL和BLLFactory類中,有一個(gè)Reflect的操作類,這是反射緩存的具體實(shí)現(xiàn)所在,我們探討一下它的實(shí)現(xiàn)。 public class Reflect<T> where T : class { private static Hashtable m_objCache = null; public static Hashtable ObjCache { get { if (m_objCache == null) { m_objCache = new Hashtable(); } return m_objCache; } } public static T Create(string sName, string sFilePath) { return Create(sName, sFilePath, true); } public static T Create(string sName, string sFilePath, bool bCache) { string CacheKey = sFilePath + "." + sName; T objType = null; if (bCache) { objType = (T)ObjCache[CacheKey]; //從緩存讀取 if (!ObjCache.ContainsKey(CacheKey)) { Assembly assObj = CreateAssembly(sFilePath); object obj = assObj.CreateInstance(CacheKey); objType = (T)obj; ObjCache.Add(CacheKey, objType);// 寫入緩存 將DAL內(nèi)某個(gè)對(duì)象裝入緩存 } } else { objType = (T)CreateAssembly(sFilePath).CreateInstance(CacheKey); //反射創(chuàng)建 } return objType; } public static Assembly CreateAssembly(string sFilePath) { Assembly assObj = (Assembly)ObjCache[sFilePath]; if (assObj == null) { assObj = Assembly.Load(sFilePath); ObjCache.Add(sFilePath, assObj);//將整個(gè)DLL裝入緩存 } return assObj; } } 另外,如果你在業(yè)務(wù)層需要實(shí)現(xiàn)更加復(fù)雜的功能,而數(shù)據(jù)庫訪問基類BaseDAL提供的函數(shù)不能滿足你的需要,可以擴(kuò)展數(shù)據(jù)訪問層的接口和實(shí)現(xiàn),如下所示。 public interface ICustomer : IBaseDAL<CustomerInfo> { List<string> GetAllCustomerNumber(); CustomerInfo GetByCustomerNumber(string number); } public class Customer : BaseDAL<CustomerInfo>, ICustomer { 對(duì)象實(shí)例及構(gòu)造函數(shù)#region 對(duì)象實(shí)例及構(gòu)造函數(shù) public static Customer Instance { get { return new Customer(); } } public Customer() : base("All_Customer","ID") { } #endregion ICustomer 成員#region ICustomer 成員 public List<string> GetAllCustomerNumber() { string sql = string.Format("Select Number From dbo.{0}", tableName); List<string> list = new List<string>(); Database db = DatabaseFactory.CreateDatabase(); DbCommand command = db.GetSqlStringCommand(sql); string number = string.Empty; using (IDataReader dr = db.ExecuteReader(command)) { while (dr.Read()) { number = dr["Number"].ToString(); if (!string.IsNullOrEmpty(number)) { list.Add(number); } } } return list; } public CustomerInfo GetByCustomerNumber(string number) { string condition = string.Format("Number = '{0}'", number); List<CustomerInfo> list = base.Find(condition); if (list.Count > 0) { return list[0]; } else { return null; } } #endregion } 那么在業(yè)務(wù)層的類修改如下 public class Customer : BaseBLL<CustomerInfo> { public Customer() : base() { } public List<string> GetAllCustomerNumber() { ICustomer customerDAL = baseDal as ICustomer; return customerDAL.GetAllCustomerNumber(); } public CustomerInfo GetByCustomerNumber(string number) { ICustomer customerDAL = baseDal as ICustomer; return customerDAL.GetByCustomerNumber(number); } } 最后,界面方面的設(shè)計(jì)是見仁見智,但根本一條是利用一些控件,可以統(tǒng)一風(fēng)格,減少勞動(dòng),給出幾個(gè)界面的設(shè)計(jì)截圖供大家參考 WinForm方面的(顏色標(biāo)明的是使用了特定的界面控件,其中紅色部分為和整個(gè)架構(gòu)整合起來的分頁控件,集成了一些基本的右鍵菜單操作,包括打印功能、數(shù)據(jù)導(dǎo)出功能等): Winform分頁控件設(shè)計(jì)視圖 可以選擇列進(jìn)行打印 在實(shí)際運(yùn)用過程中的界面效果 WebForm方面的(可以使用之前文章介紹的查詢控件、分頁控件、內(nèi)容編輯控件): 下圖是查詢控件和分頁控件的一起運(yùn)用: 修改內(nèi)容時(shí)候的編輯控件 查看內(nèi)容時(shí)候的編輯控件 以上所引用的代碼是通過代碼生成工具Database2Sharp自動(dòng)生成(http://www./Database2Sharp.htm),選擇EnterpriseLibrary架構(gòu)即可。 |
|