一般来说,对系统的分层,一般都需要下面几个层:实体层(Entity)、数据访问层(DAL)、业务逻辑层(BLL)、界面层(UI);而数据访问层,一般也会加入一个接口层(IDAL)。
在其中的实体层,一般是根据数据库进行映射外加入注释等,技术含量不大,在此一笔带过;数据库访问层和业务逻辑层,是关键之所在,因为这里好的设计,会利用很多基类的操作,减少很多代码和重复劳动;界面层,不管是WebForm还是WinForm,都是尽可能少的逻辑代码或者SQL语句在其中,好的项目可能会利用一些优秀的控件进去,提高体验,减少代码。另外,由于一些创建操作费时费资源,一般还需要把可重复利用的资源缓存起来,提高性能。
先给大家预览下项目的框架,再一层层分析讨论:
EnterpriseLib.jpg

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自动生成(
http://www.iqidi.com/Database2Sharp.htm
),选择EnterpriseLibrary架构即可。
Database2Sharp_Enterprise.jpg

标签: none

添加新评论