照搬了微软的 petshop的代码作为系统架构,petshop相信很多.net程序员都非常熟悉了,里面所展示的三层架构大意是这样的
UI层,BLL层,DAL层,UI层使用BLL层的服务对象,BLL使用DAL层的服务对象,其中BLL层对DAL层的引用使用的是接口,通过配置文件动态加载指定的DAL实现,
DAL通过使用不同的数据库ADO.NET组件,调用不同的SQL语句,如 Oracle DAL 使用了System.Data.OracleClient下的OracleConnection,OracleCommand...,
SQLServer DAL使用了System.Data.SqlClient下的SqlConnection,SqlCommand等,实现了针对不同数据库的支持。
于是,这个产品的开发人员,都是这么个过程编写代码的,首先,打开UI层,当需要对数据进行某项操作时,比如 GetProduct(int id) 编写好2条sql语句,1条sql server>sql server版(select * from product where id = @id),放置于一个专门存放sql server>sql server的sql语句的xml文件中,一条oracle版的(select * from product where id =:id),放置于一个专门存放Oracle的sql语句的xml文件中,然后在IDAL层定义方法接口,同时在SQL Server DAL和Oracle DAL各 定义一个实现方法执行该sql,
大致如下
idal:
public interface IProduct
{
product GetProduct(int id);
}
sqlserver dal:
public class ProductDal: IProduct
{
publilc product GetProduct(int id)
{
reuturn SqlHelper.ExecuteDataSet(sqlserver.sql)...
}
}
oracle dal:
public class ProductDal: IProduct
{
publilc product GetProduct(int id)
{
reuturn oracleHelper.ExecuteDataSet(sqlserver.sql)...
}
}
SQLhelper 代码大致如下
public ExecuteDataSet(string sql)
{
using(SqlConnection conn = new SqlConnection)
{
.....
}
}
Oraclehelper 代码大致如下
public ExecuteDataSet(string sql)
{
using(OracleConnection conn = new SqlConnection)
{
.....
}
}
然后再在BLL层定义方法,
idal = DALFactory.DataAccess.CreateProduct(); (通过配置文件指定返回实现的DAL)
publilc product GetProduct(int id){
reuturn idal.GetProduct(id);
}
至此,UI层终于可以调用BLL的方法了。结果就是 DAL层庞大无比,有无数个类和方法,而BLL呢,没有实质性代码,简直就是为了达到三层架构而拼凑出来似的,纯粹就是为了"三层"而"三层"。
事实上,我认为,数据层应该提供的是数据服务,即每个数据库对象(表,视图,存储过程)的crud,而和业务逻辑无关,而BLL通过调用这些数据服务,形成UI层需要的数据,并返回给UI,而UI层,只负责响应界面上用户的操作,对数据的加工处理,则交给BLL完成,这才是三层的分工职责。
像上面误解三层架构的地方其一是 没有真正理解数据访问层的负责提供数据服务的概念,实质上是把ui层需要什么都数据,都交给数据访问层去实现,petshop作为一个示例代码项目,为了通俗易懂,bll写的比较简单,却给一些人造成了误解,以为bll就是一个代理层。
其二是如果为了使产品适应不同的数据库,这种架构未免太罗嗦,首先要定义接口,然后针对sql server>sql server写一个实现方法, 然后针对oracle再写一个实现方法, 方法内容大多数情况下一模一样的,实际上微软的ADO.net组件里早就提供了一组通用的抽象基类,位于System.Data.Comm下,DbConnection,DbCommand..., System.Data.SqlCient和System.Data.OracleClient下的SqlConnection,OracleConnection均继承于对应的抽象基类,微软设置连创建这些组件的工厂类都建立好了,DbProviderFacoty,实现子类SqlClientFactory,OracleClientFactory...,事实上,我们完全可以用使用通用ADO.net组件来编写我们的
xxxDBHelper类,这样对于dal,无需使用接口,无需多个针对数据库的实现,
比如
DBHelper:
public ExecuteDataSet(string sql)
{
using(DbConnection conn = DbProviderFacory.Instance.CreateConnection())
{
....
}
DAL里不必再考虑多个库的实现,直接
DBHelper.ExecuteDataSet(sql);
我们需要做的,无非就是在初始化的时候,创建指定的DbProviderFacory,实际上配置文件的数据库连接里就有
ProviderName这一项,就是用来指定DbProviderFacory(微软其实已经提供了我们所需要的一切),而在machine.config,已经内置了微软的针对不同数据库的DbProviderFacory,如SQL Server,Oracle,OleDb,ODBC等,
说得简单点,只要我们在配置文件的数据库连接配置里,填上连接字符串,填上ProviderName,我们就能根据
ProviderName得到我们的DbProviderFacory,然后通过DbProviderFacory创建我们执行sql需要的Connction,Command等ADO.net 对象,我们在代码中无须再关心数据库的异同。
所以,我编写了DBClient,里面主要就一个类,DataAccess,提供的功能类似于petshop里面的sqlHelper,oracleHelper,只不过我使用的DbConnection,DbCommand等,
使用起来很简单,
DataAccess dataAccess = new DataAccess(providerName, connstring);
dataAccess.ExecuteDataSet(sql)....
你的providerName指向的是oracle provider,就会创建出oracle的ado组件执行sql,指向sql server>sql server provider,则会创建出sql的ado组件执行sql。
另外,在oracle provider我使用了oracle官方的oracle.dataaccess.dll,不仅性能和稳定性比微软自己的system.data.oracleclient.dll强(微软已宣布停止了该组件的发布),而且运行时带上相关的dll,还能免装
oracle 的客户端。