接着上一篇关于分层架构的讨论,
一个分层架构设计的例子(1)
。
上篇介绍了实体类(Entity)、数据库访问类(DAL)、数据访问接口(IDAL)的相关设计,本篇主要讨论下面几个部分内容:业务逻辑层、缓存机制、界面层等方面。
业务逻辑层,主要是业务逻辑基类的设计,由于数据库访问类(DAL)的基类封装了大量的操作实现,因此,业务逻辑层的主要工作是进一步封装对底层访问接口的实现,如下所示。
public
class
BaseBLL
<
T
>
where
T : BaseEntity,
new
()
{
构造函数
#region
构造函数
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
对象添加、修改、删除等接口
#region
对象添加、修改、删除等接口
/**/
///
<summary>
///
插入指定对象到数据库中
///
</summary>
///
<param name="obj">
指定的对象
</param>
///
<returns>
执行成功返回新增记录的自增长ID。
</returns>
public
virtual
bool
Insert(T obj)
{
return
baseDal.Insert(obj);
}
/**/
///
<summary>
///
更新对象属性到数据库中
///
</summary>
///
<param name="obj">
指定的对象
</param>
///
<returns>
执行成功返回
<c>
true
</c>
,否则为
<c>
false
</c>
。
</returns>
public
virtual
bool
Update(T obj,
string
primaryKeyValue)
{
return
baseDal.Update(obj, primaryKeyValue);
}
/**/
///
<summary>
///
查询数据库,检查是否存在指定ID的对象(用于字符型主键)
///
</summary>
///
<param name="key">
对象的ID值
</param>
///
<returns>
存在则返回指定的对象,否则返回Null
</returns>
public
virtual
T FindByID(
string
key)
{
return
baseDal.FindByID(key);
}
/**/
///
<summary>
///
查询数据库,检查是否存在指定键值的对象
///
</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>
///
根据指定对象的ID,从数据库中删除指定对象(用于整型主键)
///
</summary>
///
<param name="key">
指定对象的ID
</param>
///
<returns>
执行成功返回
<c>
true
</c>
,否则为
<c>
false
</c>
。
</returns>
public
virtual
bool
Delete(
string
key)
{
return
baseDal.DeleteByKey(key);
}
/**/
///
<summary>
///
根据指定条件,从数据库中删除指定对象
///
</summary>
///
<param name="condition">
删除记录的条件语句
</param>
///
<returns>
执行成功返回
<c>
true
</c>
,否则为
<c>
false
</c>
。
</returns>
public
virtual
bool
DeleteByCondition(
string
condition)
{
return
baseDal.DeleteByCondition(condition);
}
#endregion
返回集合的接口
#region
返回集合的接口
/**/
///
<summary>
///
根据ID字符串(逗号分隔)获取对象列表
///
</summary>
///
<param name="idString">
ID字符串(逗号分隔)
</param>
///
<returns>
符合条件的对象列表
</returns>
public
virtual
List
<
T
>
FindByIDs(
string
idString)
{
return
baseDal.FindByIDs(idString);
}
/**/
///
<summary>
///
根据条件查询数据库,并返回对象集合
///
</summary>
///
<param name="condition">
查询的条件
</param>
///
<returns>
指定对象的集合
</returns>
public
virtual
List
<
T
>
Find(
string
condition)
{
return
Find(condition);
}
/**/
///
<summary>
///
根据条件查询数据库,并返回对象集合(用于分页数据显示)
///
</summary>
///
<param name="condition">
查询的条件
</param>
///
<param name="info">
分页实体
</param>
///
<returns>
指定对象的集合
</returns>
public
virtual
List
<
T
>
Find(
string
condition, PagerInfo info)
{
return
baseDal.Find(condition, info);
}
/**/
///
<summary>
///
返回数据库所有的对象集合
///
</summary>
///
<returns>
指定对象的集合
</returns>
public
virtual
List
<
T
>
GetAll()
{
return
baseDal.GetAll();
}
/**/
///
<summary>
///
返回数据库所有的对象集合(用于分页数据显示)
///
</summary>
///
<param name="info">
分页实体信息
</param>
///
<returns>
指定对象的集合
</returns>
public
virtual
List
<
T
>
GetAll(PagerInfo info)
{
return
baseDal.GetAll(info);
}
public
virtual
DataSet GetAllToDataSet(PagerInfo info)
{
return
baseDal.GetAllToDataSet(info);
}
#endregion
}
业务层基类封装了大量的调用,那么对于业务层的具体操作类,它的工作就很简单了,基本上只需要继承一下基类就可以了,这就是有一个优秀父亲的好处,呵呵
public
class
Equipment : BaseBLL
<
EquipmentInfo
>
{
public
Equipment() :
base
()
{
}
}
基本上,业务层的设计到此应该收尾了,可是我们注意到,很多开发都使用了缓存的机制来进一步提高程序的性能,下面对这方面进行讨论。缓存的机制,一般是把创建过的对象资源放到一个集合中,需要的时候,调出来,如下业务层的工厂类所示。
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
"
);
//
反射创建,并缓存
}
return
bll;
}
}
}
这是一个业务逻辑类工厂创建类,我们在界面层只需要如下调用即可构造一个(利用了缓存)具体的业务类出来
CustomerInfo info
=
BLLFactory
<
Customer
>
.Instance.FindByID(ID);
在上面的BaseBLL和BLLFactory类中,有一个Reflect的操作类,这是反射缓存的具体实现所在,我们探讨一下它的实现。
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内某个对象装入缓存
}
}
else
{
objType
=
(T)CreateAssembly(sFilePath).CreateInstance(CacheKey);
//
反射创建
}
return
objType;
}
public
static
Assembly CreateAssembly(
string
sFilePath)
{
Assembly assObj
=
(Assembly)ObjCache[sFilePath];
if
(assObj
==
null
)
{
assObj
=
Assembly.Load(sFilePath);
ObjCache.Add(sFilePath, assObj);
//
将整个DLL装入缓存
}
return
assObj;
}
}
另外,如果你在业务层需要实现更加复杂的功能,而数据库访问基类BaseDAL提供的函数不能满足你的需要,可以扩展数据访问层的接口和实现,如下所示。
public
interface
ICustomer : IBaseDAL
<
CustomerInfo
>
{
List
<
string
>
GetAllCustomerNumber();
CustomerInfo GetByCustomerNumber(
string
number);
}
public
class
Customer : BaseDAL
<
CustomerInfo
>
, ICustomer
{
对象实例及构造函数
#region
对象实例及构造函数
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
}
那么在业务层的类修改如下
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);
}
}
最后,界面方面的设计是见仁见智,但根本一条是利用一些控件,可以统一风格,减少劳动,给出几个界面的设计截图供大家参考
WinForm方面的(颜色标明的是使用了特定的界面控件,其中红色部分为和整个架构整合起来的分页控件,集成了一些基本的右键菜单操作,包括打印功能、数据导出功能等):
Winform分页控件设计视图
可以选择列进行打印
在实际运用过程中的界面效果
WebForm方面的(可以使用之前文章介绍的查询控件、分页控件、内容编辑控件):
下图是查询控件和分页控件的一起运用:
修改内容时候的编辑控件
查看内容时候的编辑控件
以上所引用的代码是通过代码生成工具Database2Sharp自动生成(
http://www.iqidi.com/Database2Sharp.htm
),选择EnterpriseLibrary架构即可。