系列文章完成后 源码发布在我的GIT上 https://github.com/dubing/
文章仅代表个人观点 旨在交流 欢迎讨论
正文
按照上一篇结尾留下的话题,本篇着重对数据库操作方面也就是常用工厂模式的地方来进行泛型 委托方向的使用。
一般大型项目中大家都喜欢依赖注入的方式来数据库操作进行设计,也就是站在面向组件的层次。这里不讨论这样设计的优缺点,我们来看下如果不适用这种方式,只使用委托和泛型如何来良好的替代上面的方案。
首先分析下sql操作有哪些元素。
public class BaseDriverParam
{
public DbCommand baseCommand { get; set; }
public DbConnection baseConnection { get; set; }
public DataAdapter baseDataAdapter { get; set; }
public DataParameter baseDataParameter { get; set; }
public DbTransaction baseDbTransaction { get; set; }
public DbDataReader baseDbDataReader { get; set; }
}
这里只是一部分,当然也是最常用的部分。选择抽出这些是因为大部分数据库的驱动都支持这些。举个数据库连接的例子
public sealed class OleDbConnection : DbConnection, ICloneable, IDbConnection, IDisposable
public sealed class MySqlConnection : DbConnection, ICloneable
public sealed class SqlConnection : DbConnection, ICloneable
public sealed class SQLiteConnection : DbConnection, ICloneable
public sealed class OracleConnection : DbConnection, ICloneable
一些特殊的内容例如 DataParameter在 IDbDataParameter中并不满足需求的场合下我们选择自己填充,同样的还有下面所有的SqlCommandData等,因为不是文章主旨想表达的内容就不啰嗦了。
明白这些,下面我们就开始一步一步设计,数据库的常用操作有哪些doCreateConnection创建链接,doCreateCommand声明操作命令,doCreateDataAdapter创建数据适配器,doFillCommand执行命令等等...
这里我们做第一个选择,按照工厂模式,这些内容都是分别在各自的类中实现,那么既然我们已经摈弃了这种方案,那么我们该如何设计比较合理,用泛型么,将不同的驱动参数作为可变元素带入统一的方法中,听起来是不错的,我们先来建立方法结构
public class TBaseDriver<TCommand, TDbConnection, TDataAdapter, TDataParameter, TDbTransaction> : BaseDriver
where TCommand : DbCommand, new()
where TDbConnection : DbConnection, new()
where TDataAdapter : DbDataAdapter, new()
where TDbTransaction : DbTransaction, new()
where TDataParameter : DataParameter,new()
然后我们逐一的实现方法,在这过程中我们会发现并不是所有的方法都符合我们的需求,例如DbDataAdapter中,标准的command要分很多种类型,例如增删查改。然后对于DbDataAdapter没有一个标准的构造函数例如public SqlDataAdapter(SqlCommand selectCommand)这种形式。这样对于不同的类型又要分开操作。既然我们要选择最简洁的方法,自然这样的方式我们就先不考虑了。那么我们把眼光再网上抛一层,以方法直接作为可变元素。
public delegate string ActionDelegate();
public delegate IConnectionEx CreateConnectionExDelegate();
public delegate DbCommand CreateCommandDelegate(string dbClause);
public delegate DbConnection CreateConnectionDelegate(string dbConnection);
public delegate DbConnection CreateFrontConnectionDelegate(string dbConnection);
public delegate DbCommand FillCommandDelegate(DbCommand dbCommand, SqlCommandData sqlCD);
public delegate DataAdapter CreateDataAdapter(DbCommand dbCommand);
然而我们并非笼统的讲所有的方法都抽出,这样也就是失去了文章本来想要表达的意思。这里我们是将原来基础的方法分解,抽出可怜的逻辑设计成委托。举2个简单的例子
public DbCommand CreateCommand(SqlCommandData sql)
{
DbCommand _c = doCreateCommand(sql.SqlClause);
myConnection.Open();
if (IsTransaction && myTransaction == null)
{
myTransaction = myConnection.BeginTransaction();
}
if (IsTransaction)
{
if (myTransaction == null)
{
myTransaction = myConnection.BeginTransaction();
}
_c.Transaction = myTransaction;
}
_c.Connection = myConnection;
_c.CommandTimeout = 300;
_c.CommandType = sql.CommandType;
_c = doFillCommand(_c, sql);
return _c;
}
public DataTable Query(SqlCommandData sql)
{
using (DbCommand _c = this.CreateCommand(sql))
{
DataAdapter _s = doCreateDataAdapter(_c);
DataSet _d = new DataSet();
_s.Fill(_d);
PopuloateCommand(_c, sql);
if (!Create)
{
Dispose(true);
}
return _d.Tables[0];
}
}
那么我们在各自的驱动类中实现这里委托的逻辑,例如oracle的驱动中
public class OracleDriver : BaseDriver
{
public OracleDriver()
{
this.doCreateConnectionEx = () =>
{
MaoyaDbConnection mc = new MaoyaDbConnection();
mc.ConnectionString = this.ConnectionString;
mc.Create = true;
mc.doCreateConnection = (conn) =>
{
return new OracleConnection(conn);
};
mc.doCreateFrontConnection = (conn) =>
{
return this.CreateConnection<OracleConnection>(conn);
};
mc.doCreateCommand = (comm) =>
{
return new OracleCommand(comm);
};
mc.doCreateDataAdapter = (sqlcomm) =>
{
return new OracleDataAdapter((OracleCommand)sqlcomm);
};
mc.doFillCommand = (sqlcomm, sql) =>
{
foreach (DataParameter dp in sql.Parameters)
{
OracleParameter p = new OracleParameter();
p.ParameterName = dp.ParameterName;
p.Size = dp.Size;
p.Direction = dp.Direction;
p.IsNullable = dp.IsNullable;
p.Value = dp.Value == null ? DBNull.Value : dp.Value;
sqlcomm.Parameters.Add(p);
}
return sqlcomm;
};
return mc;
};
}
}
或者在mysql的驱动中
public class MySqlDriver : BaseDriver
{
public MySqlDriver()
{
this.doCreateConnectionEx = () =>
{
MaoyaDbConnection mc = new MaoyaDbConnection();
mc.ConnectionString = this.ConnectionString;
mc.Create = true;
mc.doCreateConnection = (conn) =>
{
return new MySqlConnection(conn);
};
mc.doCreateFrontConnection = (conn) =>
{
return this.CreateConnection<MySqlConnection>(conn);
};
mc.doCreateCommand = (comm) =>
{
return new MySqlCommand(comm);
};
mc.doCreateDataAdapter = (sqlcomm) =>
{
return new MySqlDataAdapter((MySqlCommand)sqlcomm);
};
mc.doFillCommand = (sqlcomm, sql) =>
{
foreach (DataParameter dp in sql.Parameters)
{
MySqlParameter p = new MySqlParameter();
p.ParameterName = dp.ParameterName;
p.Size = dp.Size;
p.Direction = dp.Direction;
p.IsNullable = dp.IsNullable;
p.Value = dp.Value == null ? DBNull.Value : dp.Value;
sqlcomm.Parameters.Add(p);
}
return sqlcomm;
};
return mc;
};
}
}
这么写似乎是ok了,但是我们发现各个驱动中还是有很多可以抽出通用的部分,那么我们重回泛型的概念
public class TBaseDriver<TCommand, TDbConnection, TDataAdapter, TDataParameter, TDbTransaction> : BaseDriver
where TCommand : DbCommand, new()
where TDbConnection : DbConnection, new()
where TDataAdapter : DbDataAdapter, new()
where TDbTransaction : DbTransaction, new()
where TDataParameter : DataParameter,new()
{
public TBaseDriver()
{
this.doCreateConnectionEx = () =>
{
MaoyaDbConnection mc = new MaoyaDbConnection();
mc.ConnectionString = this.ConnectionString;
mc.Create = true;
mc.doCreateConnection = (conn) =>
{
var baseConn = new TDbConnection();
baseConn.ConnectionString = conn;
return baseConn;
};
mc.doCreateFrontConnection = (conn) =>
{
return this.CreateConnection<TDbConnection>(conn);
};
mc.doCreateCommand = (comm) =>
{
var baseComm = new TCommand();
baseComm.CommandText = comm;
return baseComm;
};
mc.doFillCommand = (sqlcomm, sql) =>
{
foreach (DataParameter dp in sql.Parameters)
{
TDataParameter p = new TDataParameter();
p.ParameterName = dp.ParameterName;
p.Size = dp.Size;
p.Direction = dp.Direction;
p.IsNullable = dp.IsNullable;
p.Value = dp.Value == null ? DBNull.Value : dp.Value;
sqlcomm.Parameters.Add(p);
}
return sqlcomm;
};
return mc;
};
}
}
这里我们将可以共通的方法抽出,至于doCreateDataAdapter方法我们再各自的驱动类中实现即可。
题外
本篇到此结束,所示代码仅供参考未经测试,功能也只是部分,例如事务操作都没有阐述等等。下一篇和大家一起讨论下依赖注入的一些另类实现方法。