ADO.NET操作
使用ADO.NET的方式操作数据库时,对于经常需要操作不同数据库的同学,需要对不同的数据库翻来覆去地写操作类。
对ADO.NET,操作数据库需要有几个核心的东西(以MySql为例):
MySqlConnection
负责mysql的连接,在操作mysql前,需要先获得连接。
MySqlCommand
负责具体命令的类,具体需要执行的sql的语句需要放到它的CommandText下。
MySqlDataAdapter
对于查询数据,可以选择使用DataAdapter将数据一次性取出到DataSet或者DataTable中。
MySqlDataReader
对于查询数据,同样可以使用Reader类对数据进行读取,与Adapter不同,Reader一次取得一条数据,可以在数据获取的过程中执行代码,而不需要等待数据一次性取出。
对于mysql有以上的几个主要类,对于SQLite、SQL Server,同样类似。
可以使用一个类来将他们包装,然后编译成dll,这样如果需要操作不同的数据库,只需要通过工厂创建不同的类即可。
提取相同点
使用ADO.NET方式的数据库驱动,他们都满足这么几个特点:
- 上面列出的核心类都提供,而且都从对应的基类继承,例如MySqlConnection从SqlConnection继承。
- Command类可以通过对应Connection的CreateCommand方法生成。
那么问题就简单了,我们只要操作他们的基类就可以了。然后他们的引用通过nuget获得,这样就能正常编译。
//主要代码
///
/// 可以根据支持的Sql类型增加或删除类型,需要增加或删除对应的GetConnection和GetDbDataAdapter方法。
///
public enum SqlType
{
SqlServer,
MySql,
PostgresQL,
Oracle,
SQLite,
//对ODBC方式需要格外注意,目标系统必须预先安装有对应的数据驱动,如果使用DSN,那么还需要使用配置ODBC数据源
Odbc
}
///
/// 使用ADO.NET控制对数据库的基本访问方法,对同一个活动对象(不关闭)线程安全。
///
public class SqlManipulation : IDisposable
{
public SqlManipulation(string strDSN, SqlType sqlType)
{
_sqlType = sqlType;
_strDSN = strDSN;
}
#region private variables
private SqlType _sqlType;
private string _strDSN;
private DbConnection _conn;
private bool _disposed;
#endregion
private DbConnection GetConnection()
{
DbConnection conn;
switch (_sqlType)
{
case SqlType.SqlServer:
conn = new SqlConnection(_strDSN);
return conn;
case SqlType.MySql:
conn = new MySqlConnection(_strDSN);
return conn;
case SqlType.PostgresQL:
conn = new NpgsqlConnection(_strDSN);
return conn;
case SqlType.Oracle:
conn = new OracleConnection(_strDSN);
return conn;
case SqlType.SQLite:
conn = new SQLiteConnection(_strDSN);
return conn;
case SqlType.Odbc:
conn = new OdbcConnection(_strDSN);
return conn;
default:
return null;
}
}
private DbDataAdapter GetDbDataAdapter(string sql)
{
DbDataAdapter adp;
switch (_sqlType)
{
case SqlType.SqlServer:
adp = new SqlDataAdapter(sql, _conn as SqlConnection);
return adp;
case SqlType.MySql:
adp = new MySqlDataAdapter(sql, _conn as MySqlConnection);
return adp;
case SqlType.PostgresQL:
adp = new NpgsqlDataAdapter(sql, _conn as NpgsqlConnection);
return adp;
case SqlType.Oracle:
adp = new OracleDataAdapter(sql, _conn as OracleConnection);
return adp;
case SqlType.SQLite:
adp = new SQLiteDataAdapter(sql, _conn as SQLiteConnection);
return adp;
case SqlType.Odbc:
adp = new OdbcDataAdapter(sql, _conn as OdbcConnection);
return adp;
default:
return null;
}
}
private DbCommand GetCommand(DbConnection conn, string strSQL)
{
DbCommand command = conn.CreateCommand();
command.CommandText = strSQL;
return command;
}
///
/// 初始化连接并打开
///
///
public bool Init()
{
try
{
_conn = GetConnection();
_conn.Open();
return true;
}
catch (Exception e)
{
//记录日志,退出
MessageBox.Show(e.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
return false;
}
}
///
/// 执行SELECT查询语句,并返回DataTable对象。
///
/// 需要执行的sql语句
/// DataTable对象
public DataTable ExcuteQuery(string strSQL)
{
DbDataAdapter adp = GetDbDataAdapter(strSQL);
DataTable dt = new DataTable();
try
{
adp.Fill(dt);
}
catch (Exception e)
{
//记录日志,并返回空
MessageBox.Show(strSQL + "\n" + e.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
return null;
}
return dt;
}
///
/// 执行非Select语句,包括UPDATE DELETE INSERT
///
/// 需要执行的sql语句
/// 受影响的行数
public int ExcuteNonQuery(string strSQL)
{
//实例化OdbcCommand对象
DbCommand myCmd = GetCommand(_conn, strSQL);
try
{
//执行方法
return myCmd.ExecuteNonQuery();
}
catch (Exception e)
{
//记录日志,并返回0
MessageBox.Show(strSQL + "\n" + e.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
return 0;
}
}
///
/// 通过事务批量执行非查询SQL语句
///
/// 需要批量执行的SQL
/// 受影响的行数,发生回滚则返回-1
public int ExecuteNonQueryTransaction(List strSQLs)
{
DbCommand myCmd = GetCommand(_conn, "");
int sumAffected = 0;
DbTransaction transaction = _conn.BeginTransaction();
myCmd.Transaction = transaction;
try
{
foreach (var n in strSQLs)
{
myCmd.CommandText = n;
sumAffected += myCmd.ExecuteNonQuery();
}
transaction.Commit();
return sumAffected;
}
catch (Exception e)
{
MessageBox.Show(e.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
transaction.Rollback();
return -1;
}
}
}
- 由于不同数据库对数据类型的实现不同,不同数据库在操作command parameter上也有一些不同,所以暂时没有加入到此类中去。
- 最开始,想使用反射来达到目的,可以避免switch分支,后来想起反射需要知道Assembly名称,写这段代码还不如直接switch。
完整项目代码github地址:https://github.com/circler3/DatabaseInvoke.git
(现在已经对mysql、sql server、posgresql、sqlite和ODBC方式支持)
未来的改进
- 考虑添加EF的支持(引用使用EF的程序,主程序app.config或者web.config需要添加配置项目)
- 代码重构
- 数据库现在是持续保持打开,拟增加其他选项
写在最后
程序比较简单,代码放在github上,欢迎交流。