ADO.NET操作使用ADO.NET的方式操作資料庫時,對於經常需要操作不同資料庫的同學,需要對不同的資料庫翻來覆去地寫操作類。<br 對ADO.NET,操作資料庫需要有幾個核心的東西(以MySql為例): MySqlConnection 負責mysql的連接,在操作mysql前,需要先獲得連接.....
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獲得,這樣就能正常編譯。
//主要代碼
/// <summary>
/// 可以根據支持的Sql類型增加或刪除類型,需要增加或刪除對應的GetConnection和GetDbDataAdapter方法。
/// </summary>
public enum SqlType
{
SqlServer,
MySql,
PostgresQL,
Oracle,
SQLite,
//對ODBC方式需要格外註意,目標系統必須預先安裝有對應的數據驅動,如果使用DSN,那麼還需要使用配置ODBC數據源
Odbc
}
/// <summary>
/// 使用ADO.NET控制對資料庫的基本訪問方法,對同一個活動對象(不關閉)線程安全。
/// </summary>
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;
}
/// <summary>
/// 初始化連接並打開
/// </summary>
/// <returns></returns>
public bool Init()
{
try
{
_conn = GetConnection();
_conn.Open();
return true;
}
catch (Exception e)
{
//記錄日誌,退出
MessageBox.Show(e.Message, "錯誤", MessageBoxButtons.OK, MessageBoxIcon.Error);
return false;
}
}
/// <summary>
/// 執行SELECT查詢語句,並返回DataTable對象。
/// </summary>
/// <param name="strSQL">需要執行的sql語句</param>
/// <returns>DataTable對象</returns>
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;
}
/// <summary>
/// 執行非Select語句,包括UPDATE DELETE INSERT
/// </summary>
/// <param name="strSQL">需要執行的sql語句</param>
/// <returns>受影響的行數</returns>
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;
}
}
/// <summary>
/// 通過事務批量執行非查詢SQL語句
/// </summary>
/// <param name="strSQLs">需要批量執行的SQL</param>
/// <returns>受影響的行數,發生回滾則返回-1</returns>
public int ExecuteNonQueryTransaction(List<string> 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上,歡迎交流。