2023年6月21日发(作者:)

分层结构的⽣活例⼦_⼀个分层架构设计的例⼦⼀般来说,对系统的分层,⼀般都需要下⾯⼏个层:实体层(Entity)、数据访问层(DAL)、业务逻辑层(BLL)、界⾯层(UI);⽽数据访问层,⼀般也会加⼊⼀个接⼝层(IDAL)。在其中的实体层,⼀般是根据数据库进⾏映射外加⼊注释等,技术含量不⼤,在此⼀笔带过;数据库访问层和业务逻辑层,是关键之所在,因为这⾥好的设计,会利⽤很多基类的操作,减少很多代码和重复劳动;界⾯层,不管是WebForm还是WinForm,都是尽可能少的逻辑代码或者SQL语句在其中,好的项⽬可能会利⽤⼀些优秀的控件进去,提⾼体验,减少代码。另外,由于⼀些创建操作费时费资源,⼀般还需要把可重复利⽤的资源缓存起来,提⾼性能。先给⼤家预览下项⽬的框架,再⼀层层分析讨论:1、 实体层(定义⼀个空的基类,其他实体类继承之,主要是为了利⽤泛型操作,⽤途下⾯细说)publicclassBaseEntity{

}publicclassEquipmentInfo : BaseEntity{

Field Members#regionField Membersprivateintm_ID =0;//ID

privatestringm_PartID ="";//备件编号

//#endregionProperty Members#regionProperty Members

/**///ID///publicvirtualintID{get{returnthis.m_ID; }set{this.m_ID = value; } }/**///备件编号///publicvirtualstringPartID{get{returnthis.m_PartID; }set{this.m_PartID = value; } }//#endregion }2、 数据库访问层,数据访问层的关键是数据访问基类的设计,基类实现⼤多数数据库的⽇常操作,如下:/**///数据访问层的基类///publicabstractclassBaseDAL : IBaseDALwhereT : BaseEntity,new(){}BaseEntity就是实体类的基类,IBaseDAL是定义的数据访问基类接⼝,包含各种常⽤的操作定义;因此BaseDAL就是要对各种操作的进⾏实现,实现接⼝越多,将来继承类的重⽤程度就越⾼。以上通过泛型 ,我们就可以知道实例化那个具体访问类的信息了,可以实现强类型的函数定义。/**///⼀些基本的,作为辅助函数的接⼝///publicinterfaceIBaseDALwhereT : BaseEntity{/**///查询数据库,检查是否存在指定键值的对象//////Hashtable:键[key]为字段名;值[value]为字段对应的值///存在则返回true,否则为false。boolIsExistKey(Hashtable recordTable);/**///查询数据库,检查是否存在指定键值的对象//////指定的属性名///指定的值///存在则返回true,否则为false。boolIsExistKey(stringfieldName,objectkey);/**///获取数据库中该对象的最⼤ID值//////最⼤ID值intGetMaxID();/**///根据指定对象的ID,从数据库中删除指定对象//////指定对象的ID///执⾏成功返回true,否则为false。boolDeleteByKey(stringkey);

/**///根据条件,从数据库中删除指定对象//////删除记录的条件语句///执⾏成功返回true,否则为false。boolDeleteByCondition(stringcondition);/**///插⼊指定对象到数据库中//////指定的对象///执⾏成功返回TrueboolInsert(T obj);/**///更新对象属性到数据库中//////指定的对象///执⾏成功返回true,否则为false。boolUpdate(T obj,stringprimaryKeyValue);/**///查询数据库,检查是否存在指定ID的对象(⽤于整型主键)//////对象的ID值///存在则返回指定的对象,否则返回NullT FindByID(intkey);/**///查询数据库,检查是否存在指定ID的对象(⽤于字符型主键)//////对象的ID值///存在则返回指定的对象,否则返回NullT FindByID(stringkey);返回集合的接⼝#region返回集合的接⼝/**///根据ID字符串(逗号分隔)获取对象列表//////ID字符串(逗号分隔)///符合条件的对象列表List FindByIDs(stringidString);/**///根据条件查询数据库,并返回对象集合//////查询的条件///指定对象的集合List Find(stringcondition);/**///根据条件查询数据库,并返回对象集合(⽤于分页数据显⽰)//////查询的条件///分页实体///指定对象的集合List Find(stringcondition, PagerInfo info);/**///返回数据库所有的对象集合//////指定对象的集合List GetAll();/**///返回数据库所有的对象集合(⽤于分页数据显⽰)//////分页实体信息///指定对象的集合List GetAll(PagerInfo info); DataSet GetAllToDataSet(PagerInfo info);#endregion }细看上⾯代码,会发现由⼀个PagerInfo 的类,这个类是⽤来做分页参数传递作⽤的,根据这个参数,你可以知道具体返回那些关⼼的记录信息,这些记录⼜转换为强类型的List集合。再看看数据库访问基类的具体实现代码吧:/**///数据访问层的基类///publicabstractclassBaseDAL : IBaseDALwhereT : BaseEntity,new(){构造函数#region构造函数protectedstringtableName;//需要初始化的对象表名protectedstringprimaryKey;//数据库的主键字段名protectedstringsortField ="LastUpdated";//排序字段privateboolisDescending =true;///**///排序字段///publicstringSortField{get{returnsortField;

}set{ sortField = value;

} }/**///是否为降序///publicboolIsDescending{get{returnisDescending; }set{ isDescending = value; } }/**///数据库访问对象的表名///publicstringTableName{get{returntableName; } }/**///数据库访问对象的外键约束///publicstringPrimaryKey{get{returnprimaryKey; } }

publicBaseDAL(){}/**///指定表名以及主键,对基类进构造//////表名///表主键publicBaseDAL(stringtableName,stringprimaryKey){ame = tableName;yKey = primaryKey; }#endregion通⽤操作⽅法#region通⽤操作⽅法/**///添加记录//////Hashtable:键[key]为字段名;值[value]为字段对应的值///事务对象,如果使⽤事务,传⼊事务对象,否则为Null不使⽤事务publicboolInsert(Hashtable recordField, DbTransaction trans){(recordField, tableName, trans); }/**///添加记录//////Hashtable:键[key]为字段名;值[value]为字段对应的值///需要操作的⽬标表名称///事务对象,如果使⽤事务,传⼊事务对象,否则为Null不使⽤事务publicboolInsert(Hashtable recordField,stringtargetTable, DbTransaction trans){boolresult =false;stringfields ="";// 字段名stringvals ="";// 字段值if( recordField ==null|| <1){returnresult; } SqlParameter[] param =newSqlParameter[]; IEnumerator eKeys = merator();inti =0;while( xt() ){stringfield = ng(); fields += field +","; vals +=("@{0},", field);objectval = recordField[ng()]; param[i] =newSqlParameter("@"+ field, val); i++; } fields = (',');//除去前后的逗号vals = (',');//除去前后的逗号stringsql =("INSERT INTO {0} ({1}) VALUES ({2})", targetTable, fields, vals); Database db = Database(); DbCommand command = StringCommand(sql); ge(param);if( trans !=null){ result = eNonQuery(command, trans) >0; }else{ result = eNonQuery(command) >0; }returnresult; }

/**///更新某个表⼀条记录(只适⽤于⽤单键,⽤int类型作键值的表)//////ID号///Hashtable:键[key]为字段名;值[value]为字段对应的值///事务对象,如果使⽤事务,传⼊事务对象,否则为Null不使⽤事务publicboolUpdate(intid, Hashtable recordField, DbTransaction trans){(id, recordField, tableName, trans); }/**///更新某个表⼀条记录(只适⽤于⽤单键,⽤string类型作键值的表)//////ID号///Hashtable:键[key]为字段名;值[value]为字段对应的值///事务对象,如果使⽤事务,传⼊事务对象,否则为Null不使⽤事务publicboolUpdate(stringid, Hashtable recordField, DbTransaction trans){(id, recordField, tableName, trans); }/**///更新某个表⼀条记录(只适⽤于⽤单键,⽤int类型作键值的表)//////ID号///Hashtable:键[key]为字段名;值[value]为字段对应的值///需要操作的⽬标表名称///事务对象,如果使⽤事务,传⼊事务对象,否则为Null不使⽤事务publicboolUpdate(intid, Hashtable recordField,stringtargetTable, DbTransaction trans){returnUpdate(id, recordField, targetTable, trans); }/**///更新某个表⼀条记录(只适⽤于⽤单键,⽤string类型作键值的表)//////ID号///Hashtable:键[key]为字段名;值[value]为字段对应的值///需要操作的⽬标表名称///事务对象,如果使⽤事务,传⼊事务对象,否则为Null不使⽤事务publicboolUpdate(stringid, Hashtable recordField,stringtargetTable, DbTransaction trans){stringfield ="";// 字段名objectval =null;// 值stringsetValue ="";// 更新Set () 中的语句if( recordField ==null|| <1){returnfalse; } SqlParameter[] param =newSqlParameter[];inti =0; IEnumerator eKeys = merator();while( xt() ){ field = ng(); val = recordField[ng()]; setValue +=("{0} = @{0},", field); param[i] =newSqlParameter(("@{0}", field), val); i++; }stringsql =("UPDATE {0} SET {1} WHERE {2} = '{3}' ", targetTable, ing Database db = Database(); DbCommand command = StringCommand(sql); ge(param);boolresult =false;if(trans !=null){ result = eNonQuery(command, trans) >0; }else{ result = eNonQuery(command) >0; }returnresult; }#endregion对象添加、修改、查询接⼝#region对象添加、修改、查询接⼝/**///插⼊指定对象到数据库中//////指定的对象///执⾏成功返回新增记录的⾃增长ID。publicboolInsert(T obj){ orNullReference(obj,"传⼊的对象obj为空");

Hashtable hash = GetHashByEntity(obj);(0, -1), primaryKey, id);returnInsert(hash,null); }

/**///更新对象属性到数据库中//////指定的对象///执⾏成功返回true,否则为false。publicboolUpdate(T obj,stringprimaryKeyValue){ orNullReference(obj,"传⼊的对象obj为空");

Hashtable hash = GetHashByEntity(obj);returnUpdate(primaryKeyValue, hash,null); }

/**///查询数据库,检查是否存在指定ID的对象(⽤于整型主键)//////对象的ID值///存在则返回指定的对象,否则返回NullpublicT FindByID(intkey){

returnFindByID(ng()); }

/**///查询数据库,检查是否存在指定ID的对象(⽤于字符型主键)//////对象的ID值///存在则返回指定的对象,否则返回NullpublicT FindByID(stringkey){stringsql =("Select * From dbo.{0} Where ({1} = @ID)", tableName, primaryKey); SqlParameter param =newSqlParameter("@ID", key); Database db = Database(); DbCommand command = StringCommand(sql); (param); T entity =null;using(IDataReader dr = eReader(command)){if(()){ entity = DataReaderToEntity(dr); } }returnentity; }#endregion返回集合的接⼝#region返回集合的接⼝

/**///根据ID字符串(逗号分隔)获取对象列表//////ID字符串(逗号分隔)///符合条件的对象列表publicList FindByIDs(stringidString){stringcondition =("{0} in({1})", primaryKey, idString);(condition); }

/**///根据条件查询数据库,并返回对象集合//////查询的条件///指定对象的集合publicList Find(stringcondition){//串连条件语句为⼀个完整的Sql语句stringsql =("Select * From dbo.{0} Where ", tableName); sql += condition; sql +=(" Order by {0} {1}", sortField, isDescending ?"DESC":"ASC");

T entity =null; List list =newList(); Database db = Database(); DbCommand command = StringCommand(sql);using(IDataReader dr = eReader(command)){while(()){ entity = DataReaderToEntity(dr); (entity); } }returnlist; }

/**///根据条件查询数据库,并返回对象集合(⽤于分页数据显⽰)//////查询的条件///分页实体///指定对象的集合publicList Find(stringcondition, PagerInfo info){ List list =newList(); Database db = Database(); PagerHelper helper =newPagerHelper(tableName, condition); Count = nt(); PagerHelper helper2 =newPagerHelper(tableName,false," * ", sortField, ze, etPageIndex,true, condition);using(IDataReader dr = aReader()){while(()){ (aderToEntity(dr)); } }returnlist; }/**///返回数据库所有的对象集合//////指定对象的集合publicList GetAll(){stringsql =("Select * From dbo.{0}", tableName); sql +=(" Order by {0} {1}", sortField, isDescending ?"DESC":"ASC");

T entity =null; List list =newList(); Database db = Database(); DbCommand command = StringCommand(sql);using(IDataReader dr = eReader(command)){while(()){ entity = DataReaderToEntity(dr); (entity); } }returnlist; }

/**///返回数据库所有的对象集合(⽤于分页数据显⽰)//////分页实体信息///指定对象的集合publicList GetAll(PagerInfo info){ List list =newList();stringcondition =""; Database db = Database(); PagerHelper helper =newPagerHelper(tableName, condition); Count = nt(); PagerHelper helper2 =newPagerHelper(tableName,false," * ", sortField, ze, etPageIndex,true, condition);using(IDataReader dr = aReader()){while(()){ (aderToEntity(dr)); } }returnlist; }publicDataSet GetAllToDataSet(PagerInfo info){ DataSet ds =newDataSet();stringcondition =""; PagerHelper helper =newPagerHelper(tableName, condition); Count = nt(); PagerHelper helper2 =newPagerHelper(tableName,false," * ", sortField, ze, etPageIndex,true, condition);aSet(); }#endregion

⼦类必须实现的函数(⽤于更新或者插⼊)#region⼦类必须实现的函数(⽤于更新或者插⼊)/**///将DataReader的属性值转化为实体类的属性值,返回实体类///(提供了默认的反射机制获取信息,为了提⾼性能,建议重写该函数)//////有效的DataReader对象///实体类对象protectedvirtualT DataReaderToEntity(IDataReader dr){ T obj =newT(); PropertyInfo[] pis = e().GetProperties();foreach(PropertyInfo piinpis){try{if(dr[].ToString() !=""){ ue(obj, dr[] ??"",null); } }catch{ } }returnobj; }/**///将实体对象的属性值转化为Hashtable对应的键值(⽤于插⼊或者更新操作)///(提供了默认的反射机制获取信息,为了提⾼性能,建议重写该函数)//////有效的实体对象///包含键值映射的HashtableprotectedvirtualHashtable GetHashByEntity(T obj){ Hashtable ht =newHashtable(); PropertyInfo[] pis = e().GetProperties();for(inti =0; i

{//if (pis[i].Name != PrimaryKey){objectobjValue = pis[i].GetValue(obj,null); objValue = (objValue ==null) ? : objValue;if(!nsKey(pis[i].Name)){ (pis[i].Name, objValue); } } }returnht; }#endregion

IBaseDAL接⼝#regionIBaseDAL接⼝/**///查询数据库,检查是否存在指定键值的对象//////Hashtable:键[key]为字段名;值[value]为字段对应的值///存在则返回true,否则为false。publicboolIsExistKey(Hashtable recordTable){ SqlParameter[] param =newSqlParameter[]; IEnumerator eKeys = merator();stringfields ="";// 字段名inti =0;while(xt()){stringfield = ng(); fields +=(" {0} = @{1} AND", field, field);stringval = recordTable[ng()].ToString(); param[i] =newSqlParameter(("@{0}",field), val);

i++; } fields = ing(0, -3);//除去最后的ANDstringsql =("SELECT COUNT(*) FROM {0} WHERE {1}", tableName, fields); Database db = Database(); DbCommand command = StringCommand(sql); ge(param);return(int)eScalar(command) >0; }

/**///查询数据库,检查是否存在指定键值的对象//////指定的属性名///指定的值///存在则返回true,否则为false。publicboolIsExistKey(stringfieldName,objectkey){ Hashtable table =newHashtable(); (fieldName, key);returnIsExistKey(table); }

/**///获取数据库中该对象的最⼤ID值//////最⼤ID值publicintGetMaxID(){stringsql =("SELECT MAX({0}) AS MaxID FROM dbo.{1}", primaryKey, tableName); Database db = Database(); DbCommand command = StringCommand(sql);objectobj = eScalar(command);if(ll(obj)){return0;//没有记录的时候为0}32(obj); }

/**///根据指定对象的ID,从数据库中删除指定对象(⽤于整型主键)//////指定对象的ID///执⾏成功返回true,否则为false。publicboolDeleteByKey(stringkey){stringcondition =("{0} = '{1}'", primaryKey, key);returnDeleteByCondition(condition); }

/**///根据指定条件,从数据库中删除指定对象//////删除记录的条件语句///执⾏成功返回true,否则为false。publicboolDeleteByCondition(stringcondition){stringsql =("DELETE FROM dbo.{0} WHERE {1} ", tableName, condition); Database db = Database(); DbCommand command = StringCommand(sql);eNonQuery(command) >0; }

#endregion }3、具体的数据访问类基类完成所有的操作了,对于具体的类将是⼀⼤福⾳,说明它的⼯作减少很多了,下⾯看看具体的实现过程。定义⼀个数据访问类接⼝,然后实现接⼝和继承基类即可。publicinterfaceIEquipment : IBaseDAL{ }publicclassEquipment : BaseDAL, IEquipment{对象实例及构造函数#region对象实例及构造函数publicstaticEquipment Instance{get{returnnewEquipment(); } }publicEquipment() :base("Equipment","ID"){ }#endregion}其实这样就完成了,我们为了提⾼效率,重载两个函数的实现,避免基类的属性反射带来的性能损失,这两个函数看似很复杂,其实通过代码⽣成⼯具,⽣成起来也是毫不费功夫的。。protectedoverrideEquipmentInfo DataReaderToEntity(IDataReader dataReader)protectedoverrideHashtable GetHashByEntity(EquipmentInfo obj)因此最后的代码就变为下⾯publicclassEquipment : BaseDAL, IEquipment{对象实例及构造函数#region对象实例及构造函数publicstaticEquipment Instance{get{returnnewEquipment(); } }publicEquipment() :base("Equipment","ID"){ }#endregion/**///将DataReader的属性值转化为实体类的属性值,返回实体类//////有效的DataReader对象///实体类对象protectedoverrideEquipmentInfo DataReaderToEntity(IDataReader dataReader){ EquipmentInfo equipmentInfo =newEquipmentInfo(); SmartDataReader reader =newSmartDataReader(dataReader);

= 32("ID"); = ing("PartID"); = ing("Name"); entType = ing("EquipmentType"); ication = ing("Specification"); cturer = ing("Manufacturer"); e = es("Picture"); quipment = ing("ApplyEquipment"); unt = 32("BuyAmount"); e = eTime("BuyDate"); = ing("Status"); me = ing("UserName"); mber = 32("SafeNumber"); = ing("Note");

returnequipmentInfo; }/**///将实体对象的属性值转化为Hashtable对应的键值//////有效的实体对象///包含键值映射的HashtableprotectedoverrideHashtable GetHashByEntity(EquipmentInfo obj){ EquipmentInfo info = objasEquipmentInfo; Hashtable hash =newHashtable();

("ID", ); ("PartID", ); ("Name", ); ("EquipmentType", entType); ("Specification", ication); ("Manufacturer", cturer); ("Picture", e); ("ApplyEquipment", quipment); ("BuyAmount", unt); ("BuyDate", e); ("Status", ); ("UserName", me); ("SafeNumber", mber); ("Note", );

returnhash; } }⽂章太长,下⾯关于逻辑层、缓存、界⾯部分的设计在下⼀篇⽂章中介绍。接着上⼀篇关于分层架构的讨论,⼀个分层架构设计的例⼦(1)。上篇介绍了实体类(Entity)、数据库访问类(DAL)、数据访问接⼝(IDAL)的相关设计,本篇主要讨论下⾯⼏个部分内容:业务逻辑层、缓存机制、界⾯层等⽅⾯。业务逻辑层,主要是业务逻辑基类的设计,由于数据库访问类(DAL)的基类封装了⼤量的操作实现,因此,业务逻辑层的主要⼯作是进⼀步封装对底层访问接⼝的实现,如下所⽰。publicclassBaseBLLwhereT : BaseEntity,new(){构造函数#region构造函数privatestringdalName ="";protectedIBaseDAL baseDal =null;publicBaseBLL() :this(""){ }publicBaseBLL(stringdalName){e = dalName;if(OrEmpty(dalName)){e = GetType().Name; } baseDal = Reflect>.Create(e,""); }#endregion对象添加、修改、删除等接⼝#region对象添加、修改、删除等接⼝/**///插⼊指定对象到数据库中//////指定的对象///执⾏成功返回新增记录的⾃增长ID。publicvirtualboolInsert(T obj){(obj); }/**///更新对象属性到数据库中//////指定的对象///执⾏成功返回true,否则为false。publicvirtualboolUpdate(T obj,stringprimaryKeyValue){(obj, primaryKeyValue); }/**///查询数据库,检查是否存在指定ID的对象(⽤于字符型主键)//////对象的ID值///存在则返回指定的对象,否则返回NullpublicvirtualT FindByID(stringkey){ID(key); }/**///查询数据库,检查是否存在指定键值的对象//////指定的属性名///指定的值///存在则返回true,否则为false。publicvirtualboolIsExistKey(stringfieldName,objectkey){tKey(fieldName, key); }/**///根据指定对象的ID,从数据库中删除指定对象(⽤于整型主键)//////指定对象的ID///执⾏成功返回true,否则为false。publicvirtualboolDelete(stringkey){ByKey(key); }/**///根据指定条件,从数据库中删除指定对象//////删除记录的条件语句///执⾏成功返回true,否则为false。publicvirtualboolDeleteByCondition(stringcondition){ByCondition(condition); }#endregion返回集合的接⼝#region返回集合的接⼝/**///根据ID字符串(逗号分隔)获取对象列表//////ID字符串(逗号分隔)///符合条件的对象列表publicvirtualList FindByIDs(stringidString){IDs(idString); }/**///根据条件查询数据库,并返回对象集合//////查询的条件///指定对象的集合publicvirtualList Find(stringcondition){returnFind(condition); }/**///根据条件查询数据库,并返回对象集合(⽤于分页数据显⽰)//////查询的条件///分页实体///指定对象的集合publicvirtualList Find(stringcondition, PagerInfo info){(condition, info); }/**///返回数据库所有的对象集合//////指定对象的集合publicvirtualList GetAll(){(); }/**///返回数据库所有的对象集合(⽤于分页数据显⽰)//////分页实体信息///指定对象的集合publicvirtualList GetAll(PagerInfo info){(info); }publicvirtualDataSet GetAllToDataSet(PagerInfo info){ToDataSet(info); }#endregion }业务层基类封装了⼤量的调⽤,那么对于业务层的具体操作类,它的⼯作就很简单了,基本上只需要继承⼀下基类就可以了,这就是有⼀个优秀⽗亲的好处,呵呵publicclassEquipment : BaseBLL{publicEquipment() :base(){ } }基本上,业务层的设计到此应该收尾了,可是我们注意到,很多开发都使⽤了缓存的机制来进⼀步提⾼程序的性能,下⾯对这⽅⾯进⾏讨论。缓存的机制,⼀般是把创建过的对象资源放到⼀个集合中,需要的时候,调出来,如下业务层的⼯⼚类所⽰。publicclassBLLFactorywhereT :class{privatestaticHashtable objCache =newHashtable();publicstaticT Instance{get{stringCacheKey =typeof(T).FullName; T bll = (T)objCache[CacheKey];//从缓存读取

if(bll ==null){ bll = (typeof(T).Name,"");//反射创建,并缓存}returnbll; } } }这是⼀个业务逻辑类⼯⼚创建类,我们在界⾯层只需要如下调⽤即可构造⼀个(利⽤了缓存)具体的业务类出来CustomerInfo info = ID(ID);在上⾯的BaseBLL和BLLFactory类中,有⼀个Reflect的操作类,这是反射缓存的具体实现所在,我们探讨⼀下它的实现。publicclassReflectwhereT :class{privatestaticHashtable m_objCache =null;publicstaticHashtable ObjCache{get{if(m_objCache ==null){ m_objCache =newHashtable(); }returnm_objCache; } }publicstaticT Create(stringsName,stringsFilePath){returnCreate(sName, sFilePath,true); }publicstaticT Create(stringsName,stringsFilePath,boolbCache){stringCacheKey = sFilePath +"."+ sName; T objType =null;if(bCache){ objType = (T)ObjCache[CacheKey];//从缓存读取 if(!nsKey(CacheKey)){ Assembly assObj = CreateAssembly(sFilePath);objectobj = Instance(CacheKey); objType = (T)obj; (CacheKey, objType);// 写⼊缓存 将DAL内某个对象装⼊缓存} }else{ objType = (T)CreateAssembly(sFilePath).CreateInstance(CacheKey);//反射创建

}returnobjType; }publicstaticAssembly CreateAssembly(stringsFilePath){ Assembly assObj = (Assembly)ObjCache[sFilePath];if(assObj ==null){ assObj = (sFilePath); (sFilePath, assObj);//将整个DLL装⼊缓存}returnassObj; } }另外,如果你在业务层需要实现更加复杂的功能,⽽数据库访问基类BaseDAL提供的函数不能满⾜你的需要,可以扩展数据访问层的接⼝和实现,如下所⽰。publicinterfaceICustomer : IBaseDAL{ List GetAllCustomerNumber(); CustomerInfo GetByCustomerNumber(stringnumber); }publicclassCustomer : BaseDAL, ICustomer{对象实例及构造函数#region对象实例及构造函数publicstaticCustomer Instance{get{returnnewCustomer(); } }publicCustomer() :base("All_Customer","ID"){ }#endregion

ICustomer 成员#regionICustomer 成员publicList GetAllCustomerNumber(){stringsql =("Select Number From dbo.{0}", tableName); List list =newList(); Database db = Database(); DbCommand command = StringCommand(sql);stringnumber =;using(IDataReader dr = eReader(command)){while(()){ number = dr["Number"].ToString();if(!OrEmpty(number)){ (number); } } }returnlist; }

publicCustomerInfo GetByCustomerNumber(stringnumber){stringcondition =("Number = '{0}'", number); List list =(condition);if( >0){returnlist[0]; }else{returnnull; } }#endregion }那么在业务层的类修改如下publicclassCustomer : BaseBLL{publicCustomer() :base(){ }publicList GetAllCustomerNumber(){ ICustomer customerDAL = baseDalasICustomer;CustomerNumber(); }publicCustomerInfo GetByCustomerNumber(stringnumber){ ICustomer customerDAL = baseDalasICustomer;ustomerNumber(number); } }最后,界⾯⽅⾯的设计是见仁见智,但根本⼀条是利⽤⼀些控件,可以统⼀风格,减少劳动,给出⼏个界⾯的设计截图供⼤家参考WinForm⽅⾯的(颜⾊标明的是使⽤了特定的界⾯控件,其中红⾊部分为和整个架构整合起来的分页控件,集成了⼀些基本的右键菜单操作,包括打印功能、数据导出功能等):Winform分页控件设计视图可以选择列进⾏打印在实际运⽤过程中的界⾯效果WebForm⽅⾯的(可以使⽤之前⽂章介绍的查询控件、分页控件、内容编辑控件):下图是查询控件和分页控件的⼀起运⽤:修改内容时候的编辑控件查看内容时候的编辑控件

2023年6月21日发(作者:)

分层结构的⽣活例⼦_⼀个分层架构设计的例⼦⼀般来说,对系统的分层,⼀般都需要下⾯⼏个层:实体层(Entity)、数据访问层(DAL)、业务逻辑层(BLL)、界⾯层(UI);⽽数据访问层,⼀般也会加⼊⼀个接⼝层(IDAL)。在其中的实体层,⼀般是根据数据库进⾏映射外加⼊注释等,技术含量不⼤,在此⼀笔带过;数据库访问层和业务逻辑层,是关键之所在,因为这⾥好的设计,会利⽤很多基类的操作,减少很多代码和重复劳动;界⾯层,不管是WebForm还是WinForm,都是尽可能少的逻辑代码或者SQL语句在其中,好的项⽬可能会利⽤⼀些优秀的控件进去,提⾼体验,减少代码。另外,由于⼀些创建操作费时费资源,⼀般还需要把可重复利⽤的资源缓存起来,提⾼性能。先给⼤家预览下项⽬的框架,再⼀层层分析讨论:1、 实体层(定义⼀个空的基类,其他实体类继承之,主要是为了利⽤泛型操作,⽤途下⾯细说)publicclassBaseEntity{

}publicclassEquipmentInfo : BaseEntity{

Field Members#regionField Membersprivateintm_ID =0;//ID

privatestringm_PartID ="";//备件编号

//#endregionProperty Members#regionProperty Members

/**///ID///publicvirtualintID{get{returnthis.m_ID; }set{this.m_ID = value; } }/**///备件编号///publicvirtualstringPartID{get{returnthis.m_PartID; }set{this.m_PartID = value; } }//#endregion }2、 数据库访问层,数据访问层的关键是数据访问基类的设计,基类实现⼤多数数据库的⽇常操作,如下:/**///数据访问层的基类///publicabstractclassBaseDAL : IBaseDALwhereT : BaseEntity,new(){}BaseEntity就是实体类的基类,IBaseDAL是定义的数据访问基类接⼝,包含各种常⽤的操作定义;因此BaseDAL就是要对各种操作的进⾏实现,实现接⼝越多,将来继承类的重⽤程度就越⾼。以上通过泛型 ,我们就可以知道实例化那个具体访问类的信息了,可以实现强类型的函数定义。/**///⼀些基本的,作为辅助函数的接⼝///publicinterfaceIBaseDALwhereT : BaseEntity{/**///查询数据库,检查是否存在指定键值的对象//////Hashtable:键[key]为字段名;值[value]为字段对应的值///存在则返回true,否则为false。boolIsExistKey(Hashtable recordTable);/**///查询数据库,检查是否存在指定键值的对象//////指定的属性名///指定的值///存在则返回true,否则为false。boolIsExistKey(stringfieldName,objectkey);/**///获取数据库中该对象的最⼤ID值//////最⼤ID值intGetMaxID();/**///根据指定对象的ID,从数据库中删除指定对象//////指定对象的ID///执⾏成功返回true,否则为false。boolDeleteByKey(stringkey);

/**///根据条件,从数据库中删除指定对象//////删除记录的条件语句///执⾏成功返回true,否则为false。boolDeleteByCondition(stringcondition);/**///插⼊指定对象到数据库中//////指定的对象///执⾏成功返回TrueboolInsert(T obj);/**///更新对象属性到数据库中//////指定的对象///执⾏成功返回true,否则为false。boolUpdate(T obj,stringprimaryKeyValue);/**///查询数据库,检查是否存在指定ID的对象(⽤于整型主键)//////对象的ID值///存在则返回指定的对象,否则返回NullT FindByID(intkey);/**///查询数据库,检查是否存在指定ID的对象(⽤于字符型主键)//////对象的ID值///存在则返回指定的对象,否则返回NullT FindByID(stringkey);返回集合的接⼝#region返回集合的接⼝/**///根据ID字符串(逗号分隔)获取对象列表//////ID字符串(逗号分隔)///符合条件的对象列表List FindByIDs(stringidString);/**///根据条件查询数据库,并返回对象集合//////查询的条件///指定对象的集合List Find(stringcondition);/**///根据条件查询数据库,并返回对象集合(⽤于分页数据显⽰)//////查询的条件///分页实体///指定对象的集合List Find(stringcondition, PagerInfo info);/**///返回数据库所有的对象集合//////指定对象的集合List GetAll();/**///返回数据库所有的对象集合(⽤于分页数据显⽰)//////分页实体信息///指定对象的集合List GetAll(PagerInfo info); DataSet GetAllToDataSet(PagerInfo info);#endregion }细看上⾯代码,会发现由⼀个PagerInfo 的类,这个类是⽤来做分页参数传递作⽤的,根据这个参数,你可以知道具体返回那些关⼼的记录信息,这些记录⼜转换为强类型的List集合。再看看数据库访问基类的具体实现代码吧:/**///数据访问层的基类///publicabstractclassBaseDAL : IBaseDALwhereT : BaseEntity,new(){构造函数#region构造函数protectedstringtableName;//需要初始化的对象表名protectedstringprimaryKey;//数据库的主键字段名protectedstringsortField ="LastUpdated";//排序字段privateboolisDescending =true;///**///排序字段///publicstringSortField{get{returnsortField;

}set{ sortField = value;

} }/**///是否为降序///publicboolIsDescending{get{returnisDescending; }set{ isDescending = value; } }/**///数据库访问对象的表名///publicstringTableName{get{returntableName; } }/**///数据库访问对象的外键约束///publicstringPrimaryKey{get{returnprimaryKey; } }

publicBaseDAL(){}/**///指定表名以及主键,对基类进构造//////表名///表主键publicBaseDAL(stringtableName,stringprimaryKey){ame = tableName;yKey = primaryKey; }#endregion通⽤操作⽅法#region通⽤操作⽅法/**///添加记录//////Hashtable:键[key]为字段名;值[value]为字段对应的值///事务对象,如果使⽤事务,传⼊事务对象,否则为Null不使⽤事务publicboolInsert(Hashtable recordField, DbTransaction trans){(recordField, tableName, trans); }/**///添加记录//////Hashtable:键[key]为字段名;值[value]为字段对应的值///需要操作的⽬标表名称///事务对象,如果使⽤事务,传⼊事务对象,否则为Null不使⽤事务publicboolInsert(Hashtable recordField,stringtargetTable, DbTransaction trans){boolresult =false;stringfields ="";// 字段名stringvals ="";// 字段值if( recordField ==null|| <1){returnresult; } SqlParameter[] param =newSqlParameter[]; IEnumerator eKeys = merator();inti =0;while( xt() ){stringfield = ng(); fields += field +","; vals +=("@{0},", field);objectval = recordField[ng()]; param[i] =newSqlParameter("@"+ field, val); i++; } fields = (',');//除去前后的逗号vals = (',');//除去前后的逗号stringsql =("INSERT INTO {0} ({1}) VALUES ({2})", targetTable, fields, vals); Database db = Database(); DbCommand command = StringCommand(sql); ge(param);if( trans !=null){ result = eNonQuery(command, trans) >0; }else{ result = eNonQuery(command) >0; }returnresult; }

/**///更新某个表⼀条记录(只适⽤于⽤单键,⽤int类型作键值的表)//////ID号///Hashtable:键[key]为字段名;值[value]为字段对应的值///事务对象,如果使⽤事务,传⼊事务对象,否则为Null不使⽤事务publicboolUpdate(intid, Hashtable recordField, DbTransaction trans){(id, recordField, tableName, trans); }/**///更新某个表⼀条记录(只适⽤于⽤单键,⽤string类型作键值的表)//////ID号///Hashtable:键[key]为字段名;值[value]为字段对应的值///事务对象,如果使⽤事务,传⼊事务对象,否则为Null不使⽤事务publicboolUpdate(stringid, Hashtable recordField, DbTransaction trans){(id, recordField, tableName, trans); }/**///更新某个表⼀条记录(只适⽤于⽤单键,⽤int类型作键值的表)//////ID号///Hashtable:键[key]为字段名;值[value]为字段对应的值///需要操作的⽬标表名称///事务对象,如果使⽤事务,传⼊事务对象,否则为Null不使⽤事务publicboolUpdate(intid, Hashtable recordField,stringtargetTable, DbTransaction trans){returnUpdate(id, recordField, targetTable, trans); }/**///更新某个表⼀条记录(只适⽤于⽤单键,⽤string类型作键值的表)//////ID号///Hashtable:键[key]为字段名;值[value]为字段对应的值///需要操作的⽬标表名称///事务对象,如果使⽤事务,传⼊事务对象,否则为Null不使⽤事务publicboolUpdate(stringid, Hashtable recordField,stringtargetTable, DbTransaction trans){stringfield ="";// 字段名objectval =null;// 值stringsetValue ="";// 更新Set () 中的语句if( recordField ==null|| <1){returnfalse; } SqlParameter[] param =newSqlParameter[];inti =0; IEnumerator eKeys = merator();while( xt() ){ field = ng(); val = recordField[ng()]; setValue +=("{0} = @{0},", field); param[i] =newSqlParameter(("@{0}", field), val); i++; }stringsql =("UPDATE {0} SET {1} WHERE {2} = '{3}' ", targetTable, ing Database db = Database(); DbCommand command = StringCommand(sql); ge(param);boolresult =false;if(trans !=null){ result = eNonQuery(command, trans) >0; }else{ result = eNonQuery(command) >0; }returnresult; }#endregion对象添加、修改、查询接⼝#region对象添加、修改、查询接⼝/**///插⼊指定对象到数据库中//////指定的对象///执⾏成功返回新增记录的⾃增长ID。publicboolInsert(T obj){ orNullReference(obj,"传⼊的对象obj为空");

Hashtable hash = GetHashByEntity(obj);(0, -1), primaryKey, id);returnInsert(hash,null); }

/**///更新对象属性到数据库中//////指定的对象///执⾏成功返回true,否则为false。publicboolUpdate(T obj,stringprimaryKeyValue){ orNullReference(obj,"传⼊的对象obj为空");

Hashtable hash = GetHashByEntity(obj);returnUpdate(primaryKeyValue, hash,null); }

/**///查询数据库,检查是否存在指定ID的对象(⽤于整型主键)//////对象的ID值///存在则返回指定的对象,否则返回NullpublicT FindByID(intkey){

returnFindByID(ng()); }

/**///查询数据库,检查是否存在指定ID的对象(⽤于字符型主键)//////对象的ID值///存在则返回指定的对象,否则返回NullpublicT FindByID(stringkey){stringsql =("Select * From dbo.{0} Where ({1} = @ID)", tableName, primaryKey); SqlParameter param =newSqlParameter("@ID", key); Database db = Database(); DbCommand command = StringCommand(sql); (param); T entity =null;using(IDataReader dr = eReader(command)){if(()){ entity = DataReaderToEntity(dr); } }returnentity; }#endregion返回集合的接⼝#region返回集合的接⼝

/**///根据ID字符串(逗号分隔)获取对象列表//////ID字符串(逗号分隔)///符合条件的对象列表publicList FindByIDs(stringidString){stringcondition =("{0} in({1})", primaryKey, idString);(condition); }

/**///根据条件查询数据库,并返回对象集合//////查询的条件///指定对象的集合publicList Find(stringcondition){//串连条件语句为⼀个完整的Sql语句stringsql =("Select * From dbo.{0} Where ", tableName); sql += condition; sql +=(" Order by {0} {1}", sortField, isDescending ?"DESC":"ASC");

T entity =null; List list =newList(); Database db = Database(); DbCommand command = StringCommand(sql);using(IDataReader dr = eReader(command)){while(()){ entity = DataReaderToEntity(dr); (entity); } }returnlist; }

/**///根据条件查询数据库,并返回对象集合(⽤于分页数据显⽰)//////查询的条件///分页实体///指定对象的集合publicList Find(stringcondition, PagerInfo info){ List list =newList(); Database db = Database(); PagerHelper helper =newPagerHelper(tableName, condition); Count = nt(); PagerHelper helper2 =newPagerHelper(tableName,false," * ", sortField, ze, etPageIndex,true, condition);using(IDataReader dr = aReader()){while(()){ (aderToEntity(dr)); } }returnlist; }/**///返回数据库所有的对象集合//////指定对象的集合publicList GetAll(){stringsql =("Select * From dbo.{0}", tableName); sql +=(" Order by {0} {1}", sortField, isDescending ?"DESC":"ASC");

T entity =null; List list =newList(); Database db = Database(); DbCommand command = StringCommand(sql);using(IDataReader dr = eReader(command)){while(()){ entity = DataReaderToEntity(dr); (entity); } }returnlist; }

/**///返回数据库所有的对象集合(⽤于分页数据显⽰)//////分页实体信息///指定对象的集合publicList GetAll(PagerInfo info){ List list =newList();stringcondition =""; Database db = Database(); PagerHelper helper =newPagerHelper(tableName, condition); Count = nt(); PagerHelper helper2 =newPagerHelper(tableName,false," * ", sortField, ze, etPageIndex,true, condition);using(IDataReader dr = aReader()){while(()){ (aderToEntity(dr)); } }returnlist; }publicDataSet GetAllToDataSet(PagerInfo info){ DataSet ds =newDataSet();stringcondition =""; PagerHelper helper =newPagerHelper(tableName, condition); Count = nt(); PagerHelper helper2 =newPagerHelper(tableName,false," * ", sortField, ze, etPageIndex,true, condition);aSet(); }#endregion

⼦类必须实现的函数(⽤于更新或者插⼊)#region⼦类必须实现的函数(⽤于更新或者插⼊)/**///将DataReader的属性值转化为实体类的属性值,返回实体类///(提供了默认的反射机制获取信息,为了提⾼性能,建议重写该函数)//////有效的DataReader对象///实体类对象protectedvirtualT DataReaderToEntity(IDataReader dr){ T obj =newT(); PropertyInfo[] pis = e().GetProperties();foreach(PropertyInfo piinpis){try{if(dr[].ToString() !=""){ ue(obj, dr[] ??"",null); } }catch{ } }returnobj; }/**///将实体对象的属性值转化为Hashtable对应的键值(⽤于插⼊或者更新操作)///(提供了默认的反射机制获取信息,为了提⾼性能,建议重写该函数)//////有效的实体对象///包含键值映射的HashtableprotectedvirtualHashtable GetHashByEntity(T obj){ Hashtable ht =newHashtable(); PropertyInfo[] pis = e().GetProperties();for(inti =0; i

{//if (pis[i].Name != PrimaryKey){objectobjValue = pis[i].GetValue(obj,null); objValue = (objValue ==null) ? : objValue;if(!nsKey(pis[i].Name)){ (pis[i].Name, objValue); } } }returnht; }#endregion

IBaseDAL接⼝#regionIBaseDAL接⼝/**///查询数据库,检查是否存在指定键值的对象//////Hashtable:键[key]为字段名;值[value]为字段对应的值///存在则返回true,否则为false。publicboolIsExistKey(Hashtable recordTable){ SqlParameter[] param =newSqlParameter[]; IEnumerator eKeys = merator();stringfields ="";// 字段名inti =0;while(xt()){stringfield = ng(); fields +=(" {0} = @{1} AND", field, field);stringval = recordTable[ng()].ToString(); param[i] =newSqlParameter(("@{0}",field), val);

i++; } fields = ing(0, -3);//除去最后的ANDstringsql =("SELECT COUNT(*) FROM {0} WHERE {1}", tableName, fields); Database db = Database(); DbCommand command = StringCommand(sql); ge(param);return(int)eScalar(command) >0; }

/**///查询数据库,检查是否存在指定键值的对象//////指定的属性名///指定的值///存在则返回true,否则为false。publicboolIsExistKey(stringfieldName,objectkey){ Hashtable table =newHashtable(); (fieldName, key);returnIsExistKey(table); }

/**///获取数据库中该对象的最⼤ID值//////最⼤ID值publicintGetMaxID(){stringsql =("SELECT MAX({0}) AS MaxID FROM dbo.{1}", primaryKey, tableName); Database db = Database(); DbCommand command = StringCommand(sql);objectobj = eScalar(command);if(ll(obj)){return0;//没有记录的时候为0}32(obj); }

/**///根据指定对象的ID,从数据库中删除指定对象(⽤于整型主键)//////指定对象的ID///执⾏成功返回true,否则为false。publicboolDeleteByKey(stringkey){stringcondition =("{0} = '{1}'", primaryKey, key);returnDeleteByCondition(condition); }

/**///根据指定条件,从数据库中删除指定对象//////删除记录的条件语句///执⾏成功返回true,否则为false。publicboolDeleteByCondition(stringcondition){stringsql =("DELETE FROM dbo.{0} WHERE {1} ", tableName, condition); Database db = Database(); DbCommand command = StringCommand(sql);eNonQuery(command) >0; }

#endregion }3、具体的数据访问类基类完成所有的操作了,对于具体的类将是⼀⼤福⾳,说明它的⼯作减少很多了,下⾯看看具体的实现过程。定义⼀个数据访问类接⼝,然后实现接⼝和继承基类即可。publicinterfaceIEquipment : IBaseDAL{ }publicclassEquipment : BaseDAL, IEquipment{对象实例及构造函数#region对象实例及构造函数publicstaticEquipment Instance{get{returnnewEquipment(); } }publicEquipment() :base("Equipment","ID"){ }#endregion}其实这样就完成了,我们为了提⾼效率,重载两个函数的实现,避免基类的属性反射带来的性能损失,这两个函数看似很复杂,其实通过代码⽣成⼯具,⽣成起来也是毫不费功夫的。。protectedoverrideEquipmentInfo DataReaderToEntity(IDataReader dataReader)protectedoverrideHashtable GetHashByEntity(EquipmentInfo obj)因此最后的代码就变为下⾯publicclassEquipment : BaseDAL, IEquipment{对象实例及构造函数#region对象实例及构造函数publicstaticEquipment Instance{get{returnnewEquipment(); } }publicEquipment() :base("Equipment","ID"){ }#endregion/**///将DataReader的属性值转化为实体类的属性值,返回实体类//////有效的DataReader对象///实体类对象protectedoverrideEquipmentInfo DataReaderToEntity(IDataReader dataReader){ EquipmentInfo equipmentInfo =newEquipmentInfo(); SmartDataReader reader =newSmartDataReader(dataReader);

= 32("ID"); = ing("PartID"); = ing("Name"); entType = ing("EquipmentType"); ication = ing("Specification"); cturer = ing("Manufacturer"); e = es("Picture"); quipment = ing("ApplyEquipment"); unt = 32("BuyAmount"); e = eTime("BuyDate"); = ing("Status"); me = ing("UserName"); mber = 32("SafeNumber"); = ing("Note");

returnequipmentInfo; }/**///将实体对象的属性值转化为Hashtable对应的键值//////有效的实体对象///包含键值映射的HashtableprotectedoverrideHashtable GetHashByEntity(EquipmentInfo obj){ EquipmentInfo info = objasEquipmentInfo; Hashtable hash =newHashtable();

("ID", ); ("PartID", ); ("Name", ); ("EquipmentType", entType); ("Specification", ication); ("Manufacturer", cturer); ("Picture", e); ("ApplyEquipment", quipment); ("BuyAmount", unt); ("BuyDate", e); ("Status", ); ("UserName", me); ("SafeNumber", mber); ("Note", );

returnhash; } }⽂章太长,下⾯关于逻辑层、缓存、界⾯部分的设计在下⼀篇⽂章中介绍。接着上⼀篇关于分层架构的讨论,⼀个分层架构设计的例⼦(1)。上篇介绍了实体类(Entity)、数据库访问类(DAL)、数据访问接⼝(IDAL)的相关设计,本篇主要讨论下⾯⼏个部分内容:业务逻辑层、缓存机制、界⾯层等⽅⾯。业务逻辑层,主要是业务逻辑基类的设计,由于数据库访问类(DAL)的基类封装了⼤量的操作实现,因此,业务逻辑层的主要⼯作是进⼀步封装对底层访问接⼝的实现,如下所⽰。publicclassBaseBLLwhereT : BaseEntity,new(){构造函数#region构造函数privatestringdalName ="";protectedIBaseDAL baseDal =null;publicBaseBLL() :this(""){ }publicBaseBLL(stringdalName){e = dalName;if(OrEmpty(dalName)){e = GetType().Name; } baseDal = Reflect>.Create(e,""); }#endregion对象添加、修改、删除等接⼝#region对象添加、修改、删除等接⼝/**///插⼊指定对象到数据库中//////指定的对象///执⾏成功返回新增记录的⾃增长ID。publicvirtualboolInsert(T obj){(obj); }/**///更新对象属性到数据库中//////指定的对象///执⾏成功返回true,否则为false。publicvirtualboolUpdate(T obj,stringprimaryKeyValue){(obj, primaryKeyValue); }/**///查询数据库,检查是否存在指定ID的对象(⽤于字符型主键)//////对象的ID值///存在则返回指定的对象,否则返回NullpublicvirtualT FindByID(stringkey){ID(key); }/**///查询数据库,检查是否存在指定键值的对象//////指定的属性名///指定的值///存在则返回true,否则为false。publicvirtualboolIsExistKey(stringfieldName,objectkey){tKey(fieldName, key); }/**///根据指定对象的ID,从数据库中删除指定对象(⽤于整型主键)//////指定对象的ID///执⾏成功返回true,否则为false。publicvirtualboolDelete(stringkey){ByKey(key); }/**///根据指定条件,从数据库中删除指定对象//////删除记录的条件语句///执⾏成功返回true,否则为false。publicvirtualboolDeleteByCondition(stringcondition){ByCondition(condition); }#endregion返回集合的接⼝#region返回集合的接⼝/**///根据ID字符串(逗号分隔)获取对象列表//////ID字符串(逗号分隔)///符合条件的对象列表publicvirtualList FindByIDs(stringidString){IDs(idString); }/**///根据条件查询数据库,并返回对象集合//////查询的条件///指定对象的集合publicvirtualList Find(stringcondition){returnFind(condition); }/**///根据条件查询数据库,并返回对象集合(⽤于分页数据显⽰)//////查询的条件///分页实体///指定对象的集合publicvirtualList Find(stringcondition, PagerInfo info){(condition, info); }/**///返回数据库所有的对象集合//////指定对象的集合publicvirtualList GetAll(){(); }/**///返回数据库所有的对象集合(⽤于分页数据显⽰)//////分页实体信息///指定对象的集合publicvirtualList GetAll(PagerInfo info){(info); }publicvirtualDataSet GetAllToDataSet(PagerInfo info){ToDataSet(info); }#endregion }业务层基类封装了⼤量的调⽤,那么对于业务层的具体操作类,它的⼯作就很简单了,基本上只需要继承⼀下基类就可以了,这就是有⼀个优秀⽗亲的好处,呵呵publicclassEquipment : BaseBLL{publicEquipment() :base(){ } }基本上,业务层的设计到此应该收尾了,可是我们注意到,很多开发都使⽤了缓存的机制来进⼀步提⾼程序的性能,下⾯对这⽅⾯进⾏讨论。缓存的机制,⼀般是把创建过的对象资源放到⼀个集合中,需要的时候,调出来,如下业务层的⼯⼚类所⽰。publicclassBLLFactorywhereT :class{privatestaticHashtable objCache =newHashtable();publicstaticT Instance{get{stringCacheKey =typeof(T).FullName; T bll = (T)objCache[CacheKey];//从缓存读取

if(bll ==null){ bll = (typeof(T).Name,"");//反射创建,并缓存}returnbll; } } }这是⼀个业务逻辑类⼯⼚创建类,我们在界⾯层只需要如下调⽤即可构造⼀个(利⽤了缓存)具体的业务类出来CustomerInfo info = ID(ID);在上⾯的BaseBLL和BLLFactory类中,有⼀个Reflect的操作类,这是反射缓存的具体实现所在,我们探讨⼀下它的实现。publicclassReflectwhereT :class{privatestaticHashtable m_objCache =null;publicstaticHashtable ObjCache{get{if(m_objCache ==null){ m_objCache =newHashtable(); }returnm_objCache; } }publicstaticT Create(stringsName,stringsFilePath){returnCreate(sName, sFilePath,true); }publicstaticT Create(stringsName,stringsFilePath,boolbCache){stringCacheKey = sFilePath +"."+ sName; T objType =null;if(bCache){ objType = (T)ObjCache[CacheKey];//从缓存读取 if(!nsKey(CacheKey)){ Assembly assObj = CreateAssembly(sFilePath);objectobj = Instance(CacheKey); objType = (T)obj; (CacheKey, objType);// 写⼊缓存 将DAL内某个对象装⼊缓存} }else{ objType = (T)CreateAssembly(sFilePath).CreateInstance(CacheKey);//反射创建

}returnobjType; }publicstaticAssembly CreateAssembly(stringsFilePath){ Assembly assObj = (Assembly)ObjCache[sFilePath];if(assObj ==null){ assObj = (sFilePath); (sFilePath, assObj);//将整个DLL装⼊缓存}returnassObj; } }另外,如果你在业务层需要实现更加复杂的功能,⽽数据库访问基类BaseDAL提供的函数不能满⾜你的需要,可以扩展数据访问层的接⼝和实现,如下所⽰。publicinterfaceICustomer : IBaseDAL{ List GetAllCustomerNumber(); CustomerInfo GetByCustomerNumber(stringnumber); }publicclassCustomer : BaseDAL, ICustomer{对象实例及构造函数#region对象实例及构造函数publicstaticCustomer Instance{get{returnnewCustomer(); } }publicCustomer() :base("All_Customer","ID"){ }#endregion

ICustomer 成员#regionICustomer 成员publicList GetAllCustomerNumber(){stringsql =("Select Number From dbo.{0}", tableName); List list =newList(); Database db = Database(); DbCommand command = StringCommand(sql);stringnumber =;using(IDataReader dr = eReader(command)){while(()){ number = dr["Number"].ToString();if(!OrEmpty(number)){ (number); } } }returnlist; }

publicCustomerInfo GetByCustomerNumber(stringnumber){stringcondition =("Number = '{0}'", number); List list =(condition);if( >0){returnlist[0]; }else{returnnull; } }#endregion }那么在业务层的类修改如下publicclassCustomer : BaseBLL{publicCustomer() :base(){ }publicList GetAllCustomerNumber(){ ICustomer customerDAL = baseDalasICustomer;CustomerNumber(); }publicCustomerInfo GetByCustomerNumber(stringnumber){ ICustomer customerDAL = baseDalasICustomer;ustomerNumber(number); } }最后,界⾯⽅⾯的设计是见仁见智,但根本⼀条是利⽤⼀些控件,可以统⼀风格,减少劳动,给出⼏个界⾯的设计截图供⼤家参考WinForm⽅⾯的(颜⾊标明的是使⽤了特定的界⾯控件,其中红⾊部分为和整个架构整合起来的分页控件,集成了⼀些基本的右键菜单操作,包括打印功能、数据导出功能等):Winform分页控件设计视图可以选择列进⾏打印在实际运⽤过程中的界⾯效果WebForm⽅⾯的(可以使⽤之前⽂章介绍的查询控件、分页控件、内容编辑控件):下图是查询控件和分页控件的⼀起运⽤:修改内容时候的编辑控件查看内容时候的编辑控件