在软件开发中,数据访问层(DAL)是一个至关重要的部分,它负责应用程序与数据库之间的交互。随着应用程序的复杂度增加,数据访问层的实现也变得越来越复杂。为了简化这一过程,可以使用抽象工厂模式来创建一个灵活且可扩展的数据访问层。
System.Data命名空间主要由构成ADO.NET架构的类组成,它还定义了一系列数据访问接口,这些接口可以被不同的数据提供者使用。理想情况下,希望能够有一个数据访问层组件,它既可以用于开发时的Access 2000数据库,也可以用于生产环境中的SQL Server或Oracle数据库。
为了实现这一目标,引入了里氏替换原则(Liskov Substitution Principle),它指出派生类必须能够通过基类接口使用,而不需要用户知道具体的差异。换句话说,实现了接口的类的实例可以被向上转型为接口类型。这有助于理解一个非常有用的模式——抽象工厂模式。
抽象工厂设计模式提供了一个创建相关或依赖对象家族的契约,而不需要指定它们的具体类。
抽象工厂模式涉及以下角色:
使用抽象工厂模式有以下后果:
抽象工厂通常是单例的,通常使用工厂方法来创建产品。需要创建数据访问层对象,但也需要系统独立于每个DAL的创建方式。以下代码块显示了代表抽象产品的IDal接口。
public interface IDal
{
IDbCommand CreateCommand();
IDbCommand CreateCommand(string cmdText);
IDbCommand CreateCommand(string cmdText, IDbConnection cn);
IDbCommand CreateCommand(string cmdText, IDbConnection cn, IDbTransaction trans);
IDbConnection CreateConnection();
IDbConnection CreateConnection(string cnString);
IDbDataAdapter CreateDataAdapter();
IDbDataAdapter CreateDataAdapter(IDbCommand selectCmd);
IDbDataAdapter CreateDataAdapter(string selectCmdText, string selectCnString);
IDbDataAdapter CreateDataAdapter(string selectCmdText, IDbConnection selectCn);
IDataReader CreateDataReader(IDbCommand dbCmd);
IDataReader CreateDataReader(IDbCommand dbCmd, CommandBehavior dbCmdBehavior);
}
为了使输入/输出参数独立于实现,使用了公共接口(System.Data)作为参数类型。现在是时候实现扩展IDAL接口的实际产品了。为此,需要创建一个新类,将称之为OracleDal。为什么叫OracleDal?因为想构建一个Oracle访问点。当然,可以看到附上的完整源代码,并点击其他图标,可以使用许多其他提供者创建不同的对象。以下示例显示了一个简化的OracleDal实现。
public class OracleDal : IDal
{
public IDbCommand CreateCommand(string cmdText, IDbConnection cn, IDbTransaction trans)
{
IDbCommand oracleCmd = null;
try
{
oracleCmd = new OracleCommand(cmdText, (OracleConnection)cn, (OracleTransaction)trans);
}
catch (OracleException oracleExc)
{
if (oracleCmd != null)
oracleCmd.Dispose();
throw new Exception(oracleExc.Message);
}
return oracleCmd;
}
public IDataReader CreateDataReader(IDbCommand dbCmd, CommandBehavior dbCmddBehavior)
{
IDataReader dr = null;
try
{
dr = dbCmd.ExecuteReader(dbCmddBehavior);
}
catch (OracleException oracleExc)
{
if (dr != null)
{
if (!dr.IsClosed)
dr.Close();
dr.Dispose();
}
throw new Exception(oracleExc.Message);
}
return dr;
}
public IDbConnection CreateConnection(string cnString)
{
IDbConnection oracleCn = null;
try
{
oracleCn = new OracleConnection(cnString);
}
catch (OracleException oracleExc)
{
if (oracleCn != null)
oracleCn.Dispose();
throw new Exception(oracleExc.Message);
}
return oracleCn;
}
public IDbDataAdapter CreateDataAdapter(string selectCmdText, IDbConnection selectCn)
{
IDbDataAdapter oracleDataAdapter = null;
try
{
oracleDataAdapter = new OracleDataAdapter(selectCmdText, (OracleConnection)selectCn);
}
catch (OracleException oracleExc)
{
throw new Exception(oracleExc.Message);
}
return oracleDataAdapter;
}
}
public class DB2Dal : IDal
{
public IDbCommand CreateCommand(string cmdText, IDbConnection cn, IDbTransaction trans)
{
IDbCommand db2Cmd = null;
try
{
db2Cmd = new iDB2Command(cmdText, (db2Connection)cn, (iDB2Transaction)trans);
}
catch (iDB2Exception db2Exc)
{
if (db2Cmd != null)
db2Cmd.Dispose();
throw new Exception(db2Exc.Message);
}
return db2Cmd;
}
public IDataReader CreateDataReader(IDbCommand dbCmd, CommandBehavior dbCmddBehavior)
{
IDataReader dr = null;
try
{
dr = dbCmd.ExecuteReader(dbCmddBehavior);
}
catch (iDB2Exception db2Exc)
{
if (dr != null)
{
if (!dr.IsClosed)
dr.Close();
dr.Dispose();
}
throw new Exception(db2Exc.Message);
}
return dr;
}
public IDbConnection CreateConnection(string cnString)
{
IDbConnection db2Cn = null;
try
{
db2Cn = new iDB2Connection(cnString);
}
catch (iDB2Exception db2Exc)
{
if (db2Cn != null)
db2Cn.Dispose();
throw new Exception(db2Exc.Message);
}
return db2Cn;
}
public IDbDataAdapter CreateDataAdapter(string selectCmdText, IDbConnection selectCn)
{
IDbDataAdapter db2DataAdapter = null;
try
{
db2DataAdapter = new iDB2DataAdapter(selectCmdText, (iDB2Connection)selectCn);
}
catch (iDB2Exception db2Exc)
{
throw new Exception(db2Exc.Message);
}
return db2DataAdapter;
}
}
public class SqlDal : IDal
{
public IDbCommand CreateCommand(string cmdText, IDbConnection cn, IDbTransaction trans)
{
IDbCommand sqlCmd = null;
try
{
sqlCmd = new SqlCommand(cmdText, (SqlConnection)cn, (SqlTransaction)trans);
}
catch (SqlException sqlExc)
{
if (sqlCmd != null)
sqlCmd.Dispose();
throw new Exception(sqlExc.Message);
}
return sqlCmd;
}
public IDataReader CreateDataReader(IDbCommand dbCmd, CommandBehavior dbCmddBehavior)
{
IDataReader dr = null;
try
{
dr = dbCmd.ExecuteReader(dbCmddBehavior);
}
catch (SqlException sqlExc)
{
if (dr != null)
{
if (!dr.IsClosed)
dr.Close();
dr.Dispose();
}
throw new Exception(sqlExc.Message);
}
return dr;
}
public IDbConnection CreateConnection(string cnString)
{
IDbConnection sqlCn = null;
try
{
sqlCn = new SqlConnection(cnString);
}
catch (SqlException sqlExc)
{
if (sqlCn != null)
sqlCn.Dispose();
throw new Exception(sqlExc.Message);
}
return sqlCn;
}
public IDbDataAdapter CreateDataAdapter(string selectCmdText, IDbConnection selectCn)
{
IDbDataAdapter sqlDataAdapter = null;
try
{
sqlDataAdapter = new SqlDataAdapter(selectCmdText, (SqlConnection)selectCn);
}
catch (SqlException sqlExc)
{
throw new Exception(sqlExc.Message);
}
return sqlDataAdapter;
}
}