一般来说,对系统的分层,一般都需要下面几个层:实体层(Entity)、数据访问层(DAL)、业务逻辑层(BLL)、界面层(UI);而数据访问层,一般也会加入一个接口层(IDAL)。 在其中的实体层,一般是根据数据库进行映射外加入注释等,技术含量不大,在此一笔带过;数据库访问层和业务逻辑层,是关键之所在,因为这里好的设计,会利用很多基类的操作,减少很多代码和重复劳动;界面层,不管是WebForm还是WinForm,都是尽可能少的逻辑代码或者SQL语句在其中,好的项目可能会利用一些优秀的控件进去,提高体验,减少代码。另外,由于一些创建操作费时费资源,一般还需要把可重复利用的资源缓存起来,提高性能。 先给大家预览下项目的框架,再一层层分析讨论: 1、 实体层(定义一个空的基类,其他实体类继承之,主要是为了利用泛型操作,用途下面细说) public class BaseEntity { } public class EquipmentInfo : BaseEntity { Field Members Property Members } 2、 数据库访问层,数据访问层的关键是数据访问基类的设计,基类实现大多数数据库的日常操作,如下:
/// <summary> /// 数据访问层的基类 /// </summary> public abstract class BaseDAL < T > : IBaseDAL < T > where T : BaseEntity, new () { } BaseEntity就是实体类的基类,IBaseDAL是定义的数据访问基类接口,包含各种常用的操作定义;因此BaseDAL就是要对各种操作的进行实现,实现接口越多,将来继承类的重用程度就越高。
以上通过泛型<T> ,我们就可以知道实例化那个具体访问类的信息了,可以实现强类型的函数定义。
/// <summary> /// 一些基本的,作为辅助函数的接口 /// </summary> public interface IBaseDAL < T > where T : BaseEntity { /// <summary> /// 查询数据库,检查是否存在指定键值的对象 /// </summary> /// <param name="recordTable">Hashtable:键[key]为字段名;值[value]为字段对应的值</param> /// <returns>存在则返回<c>true</c>,否则为<c>false</c>。</returns> bool IsExistKey(Hashtable recordTable); /// <summary> /// 查询数据库,检查是否存在指定键值的对象 /// </summary> /// <param name="fieldName">指定的属性名</param> /// <param name="key">指定的值</param> /// <returns>存在则返回<c>true</c>,否则为<c>false</c>。</returns> bool IsExistKey(string fieldName, object key); /// <summary> /// 获取数据库中该对象的最大ID值 /// </summary> /// <returns>最大ID值</returns> int GetMaxID(); /// <summary> /// 根据指定对象的ID,从数据库中删除指定对象 /// </summary> /// <param name="key">指定对象的ID</param> /// <returns>执行成功返回<c>true</c>,否则为<c>false</c>。</returns> bool DeleteByKey(string key); /// <summary> /// 根据条件,从数据库中删除指定对象 /// </summary> /// <param name="condition">删除记录的条件语句</param> /// <returns>执行成功返回<c>true</c>,否则为<c>false</c>。</returns> bool DeleteByCondition(string condition); /// <summary> /// 插入指定对象到数据库中 /// </summary> /// <param name="obj">指定的对象</param> /// <returns>执行成功返回True</returns> bool Insert(T obj); /// <summary> /// 更新对象属性到数据库中 /// </summary> /// <param name="obj">指定的对象</param> /// <returns>执行成功返回<c>true</c>,否则为<c>false</c>。</returns> bool Update(T obj, string primaryKeyValue); /// <summary> /// 查询数据库,检查是否存在指定ID的对象(用于整型主键) /// </summary> /// <param name="key">对象的ID值</param> /// <returns>存在则返回指定的对象,否则返回Null</returns> T FindByID(int key); /// <summary> /// 查询数据库,检查是否存在指定ID的对象(用于字符型主键) /// </summary> /// <param name="key">对象的ID值</param> /// <returns>存在则返回指定的对象,否则返回Null</returns> T FindByID(string key); 返回集合的接口 } 细看上面代码,会发现由一个PagerInfo 的类,这个类是用来做分页参数传递作用的,根据这个参数,你可以知道具体返回那些关心的记录信息,这些记录又转换为强类型的List<T>集合。
再看看数据库访问基类的具体实现代码吧:
/// <summary> /// 数据访问层的基类 /// </summary> public abstract class BaseDAL<T> : IBaseDAL<T>
where T : BaseEntity,
new()
{
构造函数 通用操作方法 对象添加、修改、查询接口 返回集合的接口 子类必须实现的函数(用于更新或者插入) IBaseDAL接口 }
3、具体的数据访问类
基类完成所有的操作了,对于具体的类将是一大福音,说明它的工作减少很多了,下面看看具体的实现过程。定义一个数据访问类接口,然后实现接口和继承基类即可。
public interface IEquipment : IBaseDAL < EquipmentInfo > { } public class Equipment : BaseDAL < EquipmentInfo > , IEquipment { 对象实例及构造函数} 其实这样就完成了,我们为了提高效率,重载两个函数的实现,避免基类的属性反射带来的性能损失,这两个函数看似很复杂,其实通过代码生成工具,生成起来也是毫不费功夫的。。
protected override EquipmentInfo DataReaderToEntity(IDataReader dataReader) protected override Hashtable GetHashByEntity(EquipmentInfo obj) 因此最后的代码就变为下面
public class Equipment : BaseDAL < EquipmentInfo > , IEquipment { 对象实例及构造函数 /// <summary> /// 将DataReader的属性值转化为实体类的属性值,返回实体类 /// </summary> /// <param name="dr">有效的DataReader对象</param> /// <returns>实体类对象</returns> protected override EquipmentInfo DataReaderToEntity(IDataReader dataReader) { EquipmentInfo equipmentInfo = new EquipmentInfo(); SmartDataReader reader = new SmartDataReader(dataReader); equipmentInfo.ID = reader.GetInt32("ID"); equipmentInfo.PartID = reader.GetString("PartID"); equipmentInfo.Name = reader.GetString("Name"); equipmentInfo.EquipmentType = reader.GetString("EquipmentType"); equipmentInfo.Specification = reader.GetString("Specification"); equipmentInfo.Manufacturer = reader.GetString("Manufacturer"); equipmentInfo.Picture = reader.GetBytes("Picture"); equipmentInfo.ApplyEquipment = reader.GetString("ApplyEquipment"); equipmentInfo.BuyAmount = reader.GetInt32("BuyAmount"); equipmentInfo.BuyDate = reader.GetDateTime("BuyDate"); equipmentInfo.Status = reader.GetString("Status"); equipmentInfo.UserName = reader.GetString("UserName"); equipmentInfo.SafeNumber = reader.GetInt32("SafeNumber"); equipmentInfo.Note = reader.GetString("Note"); return equipmentInfo; } /// <summary> /// 将实体对象的属性值转化为Hashtable对应的键值 /// </summary> /// <param name="obj">有效的实体对象</param> /// <returns>包含键值映射的Hashtable</returns> protected override Hashtable GetHashByEntity(EquipmentInfo obj) { EquipmentInfo info = obj as EquipmentInfo; Hashtable hash = new Hashtable(); hash.Add("ID", info.ID); hash.Add("PartID", info.PartID); hash.Add("Name", info.Name); hash.Add("EquipmentType", info.EquipmentType); hash.Add("Specification", info.Specification); hash.Add("Manufacturer", info.Manufacturer); hash.Add("Picture", info.Picture); hash.Add("ApplyEquipment", info.ApplyEquipment); hash.Add("BuyAmount", info.BuyAmount); hash.Add("BuyDate", info.BuyDate); hash.Add("Status", info.Status); hash.Add("UserName", info.UserName); hash.Add("SafeNumber", info.SafeNumber); hash.Add("Note", info.Note); return hash; } } 文章太长,下面关于逻辑层、缓存、界面部分的设计在下一篇文章中介绍。
以上所引用的代码是通过代码生成工具Database2Sharp自动生成(),选择EnterpriseLibrary架构即可。
本文转自博客园伍华聪的博客,原文链接:,如需转载请自行联系原博主。