.Net PetShop3.0的数据访问技术及其改进
WWW.ISEAST.COM
使用ASP.NET开发网站,比起以前的ASP来,在界面设计和事件处理上有了很大改进。另外引入了(code behind)简化了页面文件中的代码,使我们能摆脱以前ASP页面中界面与VB Script交织的困惑。再通过把公用代码封装在用户控件(UserControl),在需要引用页面代码中加入Register 来引用控件即可,这有些像Java的Tag技术。
但是如果要实现企业应用,只应用界面的改进技术是远远不够的,更重要的是要实现数据库访问和传递方面的改进。为了示范Ado.Net的数据库编程模式,并且在性能上的不输于Java的J2EE,迅速提供了.Net版本的PetShop。虽然关于这两种技术实现PetShop的优劣争论不断,在这里试图分析PetShop数据访问技术的优点和不足,并提出改进的方法。
首先从http://www.gotdotnet.com/team/compare/下载最新的PetShop 3.0并安装后,其系统结构目录如下:
其目录按逻辑很清晰的分为业务逻辑层(BLL),数据访问层(DAL),实体层(MODEL),为了能同时访问Oracle和SqlServer,通过DALFactory根据配置文件来创建不同数据库的DAL对象来实现,而执行数据库Sql操作都放在一个Helper类中,如访问Oracle的用的就是OraHelper,它封装了所有对数据库的底层操作。下面是示例代码段:
public static int ExecuteNonQuery(OracleConnection conn, CommandType cmdType, string cmdText, params OracleParameter[] cmdParms)
{
OracleCommand cmd = new OracleCommand();
PrepareCommand(cmd, conn, null, cmdType, cmdText, cmdParms);
int val = cmd.ExecuteNonQuery();
cmd.Parameters.Clear();
return val;
}
我们看到了PetShop的数据访问方式,把所有数据读取,修改,新增的操作都用执行SQL语句来实现,并直接返回一个DataReader,接着DataReader是这样被填充到一个对象数组中:
public IList GetItemsByProduct(string productId)
{
// Declare array to return
IList itemsByProduct = new ArrayList();
// Create a database parameter
OracleParameter parm = new OracleParameter(PARM_PRODUCT_ID,OracleType.Char, 10);
//Set the parameter value
parm.Value = productId;
//Execute the query
using (OracleDataReader rdr = OraHelper.ExecuteReader(OraHelper.CONN_STRING_NON_DTC, CommandType.Text, SQL_SELECT_ITEMS_BY_PRODUCT, parm)) {
// Scroll through the results
while (rdr.Read()){
ItemInfo item = new ItemInfo(rdr.GetString(0).Trim(), rdr.GetString(1), rdr.GetDecimal(2), rdr.GetString(3), null);
//Add each item to the arraylist
itemsByProduct.Add(item);
}
}
return itemsByProduct;
}
这个方法通过OraHelper查询到数据,然后用ItemInfo item = new ItemInfo(rdr.GetString(0).Trim(), rdr.GetString(1), rdr.GetDecimal(2), rdr.GetString(3), null);填充完数据的具体对象,就此.Net PetShop完成了数据的O/R映射,并返回了填充完对象的ArrayList。
从以上.Net PetShop的数据访问实现方式来看有以下特点:
实现了多种数据库访问,并隔离了数据访问的具体实现方式
数据访问层和业务逻辑层的严格区分
用DataReader填充具体对象,而不是传递DataSet,节省了服务器资源;
但是这种方式也有它的不足之处:
在代码中出现大量的新增,删除和更新Sql语句,没有用一个类来实现这些通用的操作;
有大量参数赋值语句,可读性差,不利于代码的后期的维护;
从DataReader填充到对象属性需要重复写代码;
针对这些问题,通过应用Attribute和类反射机制,新增,删除和更新功能不需要再写Sql语句,参数赋值也不需要逐一写赋值过程,大大提高了编写数据访问代码的效率。
下面详细介绍其实现方式:
首先我们定义了以下Attribute,其主要代码如下:
public class DataDynamic : Attribute
{
public DataDynamic(string fieldName,bool isKey)
{
_fieldName = fieldName;
_isKey = isKey;
}
/// <summary>
/// The name of the field in the database
/// </summary>
…
}
private string _fieldName = "";
private bool _isKey = false;
private string _oldValue = "null";
}
在这个自定义属性中,描述了在数据库中的字段信息:fieldName(字段名),isKey(是否主键),oldValue(修改前的值);
然后在定义实体对象时需要定义其属性如下:
public class NewsObject
{
private string id = "NewId";
private string subject = "";
/// <summary>
/// The Id in the database
/// </summary>
[DataDynamic("id",true)]
public string Id
{
get
{
return id;
}
set
{
id = value;
}
}
/// <summary>
/// The name in the database
/// </summary>
[DataDynamic("subject",false)]
public string Subject
{
get
{
return subject;
}
set
{
subject = value;
}
}
}
在这里定义id为表主键,在BaseDataProvider数据访问组件中就是依靠这些信息自动产生更新和删除的Sql语句,而不再需要为每个对象个别定义Sql语句。访问该数据对象的代码变为:
BaseDataProvider dataProvider = new BaseDataProvider(null," NewsObject");
在BaseDataProvider中会获取数据并自动填充到NewsObject中,而删除一个对象则变为dataProvider.Delete(i),调用这个方法将自动删除相应的NewsObject,并产生删除数据库相应记录的Sql语句:delete NewsObject where id = 3。如果对某个NewsObject的信息做了修改,也只需调用dataProvider.Update(),就会产生相应的update语句,把数据自动更新到数据库。
可见,通过这样的改进,能减少重复代码的编写,提高系统实现效率。但是也要付出一定的性能代价,因为运用类反射会消耗一定的系统资源。究竟孰优孰劣,还是个人权衡吧。