如何打造自己的數據訪問層二
當我們通過上篇《打造自己的數據訪問層一》已了解了.NET對數據庫操作的基本原理,并就Ado.net對象的使用提出了幾點疑問:
1、如何由系統來判斷數據庫型。
2、如何消除這些重復代碼。
而上篇中也提出了一種解決思路,對ADO.NET對象進行封裝,具體應該如何實施?
1、需要一個對象,該對象用于建立內存表與物理表的之間映射關系,解決數據查詢、更新操作,形成了數據映射對象,定義為DataMapping。
2、每一個映射對象只與一張物理建立映射關系,如果有多個這樣的對象同時操作,如何解決?這時就需要另一個對象,用于添加映射對象集合,打包映射對象操作,形成了數據執行者,定義為DataExecutor。
想想看,只需要這兩個基本對象,就可以形成簡單的數據訪問層了。
先實現DataMapping,它應具備如下功能。
1、需要知道物理表的信息,表名、主鍵集、字段集。
2、需要知道映射的是什么類型的數據庫,MSSql數據庫、Oracle數據庫、MySql數據庫或者其他類型的數據庫。
3、可查詢數據,即填充內存表。
4、可更新數據,并且可設置更新操作方式。
5、可加入到事務中去。
根據上述功能,可初步設計出的DataMapping類:
public class DataMapping
{
public DataMapping()
{ }
/// <summary>
/// 填充數據集
/// </summary>
public void Fill()
{
}
/// <summary>
/// 設置更新命令
/// </summary>
public void SetCommands()
{
}
/// <summary>
/// 設置數據提交事務
/// </summary>
public void SetTransaction()
{
}
/// <summary>
/// 提交數據
/// </summary>
public bool Update()
{
}
/// <summary>
/// 更新列名
/// </summary>
public string Columns
{
get
{
return columns;
}
set
{
columns = value;
}
}
private string columns = "";
/// <summary>
/// 主鍵名
/// </summary>
public string KeyColumns
{
get
{
return keyColumns;
}
set
{
keyColumns = value;
}
}
private string keyColumns = "";
/// <summary>
/// 表名
/// </summary>
public string TableName
{
get
{
return tableName;
}
set
{
tableName = value;
}
}
private string tableName = "";
}
再來實現DataExecutor類,它應具備的功能:
1、應該知道執行什么類型的數據庫操作。
2、可以添加映射對象。
3、可以進行數據提交。
如何來知道執行的數據庫類型,我們可以定義具體的執行者,比如MSSql執行者、Oracle執行者、MySql執行者。
可以初步設計出DataExecutor類
public abstract class DataExecutor
{
private IList<DataMapping> lisDataMappings = new List<DataMapping>();
/// <summary>
/// 添加數據映射對象
/// </summary>
public void AddDataMapping(DataMapping map)
{
}
/// <summary>
/// 更新數據
/// </summary>
public bool Update()
{
}
}
public class MSSqlExecutor : DataExecutor
{
}
public class OracleExecutor : DataExecutor
{
}
public class MySqlExecutor : DataExecutor
{
}
準備就緒,開始行具體設計。
先從DataMapping的Fill方法入手,看看它是如何查詢數據的。
public void Fill(string sqlText, string tableName, DataSet ds)
{
IDbConnection conn = 具體的數據連接對象;
IDbDataAdapter dataAdapter = 具體的數據適配對象;
IDbCommand cmd = 具體的命令對象;
cmd.Connection = conn;
cmd.CommandText = sqlText;
dataAdapter.SelectCommand = cmd;
((DbDataAdapter)dataAdapter).Fill(ds, tableName);
}
問題出來了,這里出現了具體的對象,如何得到這些對象?
前面我們設計了MSSqlExecutor類,它已經知道具體的數據庫類型,所以它也應該知道進行數據操作的具體的對象,DataMapping類是否可以引用該它,從而通過它來獲取數據操作對象,因此,可以MSSqlExecutor類及DataMapping類進行修改,使DataMapping對MSSqlExecutor類產生依賴關系;這只是對MSSql數據庫進行操作,現要改變數據庫對象為Oracle了,DataMapping類應該也需要對OracleExecutor類產生依賴關系。
因此,這里可以設計一個接口,用于獲取具體對象:
/// <summary>
/// 映射執行接口
/// </summary>
public interface IMappingExecute
{
/// <summary>
/// 獲取連接對象
/// </summary>
IDbConnection GetConn();
/// <summary>
/// 獲取數據適配器
/// </summary>
IDbDataAdapter GetDataAdapter();
/// <summary>
/// 獲取命令對象
/// </summary>
IDbCommand GetCommand();
/// <summary>
/// 獲取命令參數
/// </summary>
IDbDataParameter GetDataParameter(string col);
/// <summary>
/// 獲取命令參數
/// 數據庫之間的命令參類是不一樣的
/// MMSql是“@” + 列名,Oracle是 “:” + 列名,MySql是 “?” + 列名
/// </summary>
string GetSourceColumn(string col);
}
改造后的MSSqlExecutor類為:
public class MSSqlExecutor : DataExecutor, IMappingExecute
{
}
改造后的DataMapping類為:
到此為止,查詢功能算是完成了,接下來該實現更新功能了,從SetCommands入手,以新增操作為例:public class DataMapping
{
private IDbConnection conn = null;
private IDbDataAdapter dataAdapter = null;
/// <summary>
/// 映射執行對象
/// </summary>
public IMappingExecute ExecuteObject
{
set
{
executeObj = value;
conn = executeObj.GetConn();
}
}
private IMappingExecute executeObj;
/// <summary>
/// 填充數據集
/// 參數:查詢語句
/// 參數:內存表名
/// </summary>
public void Fill(string sqlText, string tableName, DataSet ds)
{
dataAdapter = executeObj.GetDataAdapter();
IDbCommand cmd = executeObj.GetCommand();
cmd.Connection = conn;
cmd.CommandText = sqlText;
dataAdapter.SelectCommand = cmd;
((DbDataAdapter)dataAdapter).Fill(ds, tableName);
}
}
/// <summary>
/// 設置更新命令
/// </summary>
public void SetCommands(DataCommandType commandType, DataSet ds)
{
if ((commandType & DataCommandType.Insert) == DataCommandType.Insert)
{
CreateInsertCommand(ds);
}
if ((commandType & DataCommandType.Update) == DataCommandType.Update)
{
CreateUpdateCommand(ds);
}
if ((commandType & DataCommandType.Delete) == DataCommandType.Delete)
{
CreateDeleteCommand(ds);
}
}
/// <summary>
/// 生成新增命令及SQL語句
/// </summary>
private void CreateInsertCommand(DataSet ds)
{
IList<string> lisColumns = GetColumns(ds);
StringBuilder sbCol = new StringBuilder();
StringBuilder sbVal = new StringBuilder();
foreach (string col in lisColumns)
{
sbCol.AppendFormat(", {0}", col);
sbVal.AppendFormat(", {0}", executeObj.GetSourceColumn(col));
}
sbCol.Remove(0, 2);
sbVal.Remove(0, 2);
string sqlText = string.Format("INSERT INTO {0} ({1}) VALUES ({2})", tableName, sbCol.ToString(), sbVal.ToString());
IDbCommand cmd = executeObj.GetCommand();
cmd.Connection = conn;
cmd.CommandText = sqlText;
SetCommandParams(cmd, lisColumns);
dataAdapter.InsertCommand = cmd;
}
/// <summary>
/// 獲取列字段集
/// </summary>
private IList<string> GetColumns(DataSet ds)
{
IList<string> lisColumns = new List<string>();
if (columns != "*")
{
string[] sltCol = columns.Split(',');
foreach (string col in sltCol)
{
lisColumns.Add(col.Trim());
}
}
else
{
DataTable dt = ds.Tables[tableName];
foreach (DataColumn dc in dt.Columns)
{
lisColumns.Add(dc.ColumnName);
}
}
return lisColumns;
}
更新操作非常簡單,就是在上一篇的數據操作原理的基礎上動態生成了查詢語句及參數綁定,不多做解釋。
其中DataCommandType為自定義枚舉類型:
/// <summary>
/// 數據操作命令類型
/// </summary>
public enum DataCommandType
{
/// <summary>
/// 新增
/// </summary>
Insert = 1,
/// <summary>
/// 修改
/// </summary>
Update = 2,
/// <summary>
/// 刪除
/// </summary>
Delete = 4
}
更新完后進行數據提交:
/// <summary>
/// 提交數據
/// </summary>
public bool Update(DataSet ds)
{
return ((DbDataAdapter)dataAdapter).Update(ds, tableName) > 0;
}
至此,數據更新操作也已經完成,***再看看數據執行者是如何進行批量提交。
這里產生的***個問題是,什么時候數據執行者會人將映射對象加入到集合中來,其中一種方法是在DataMapping設置更新的時候自己加入到集合去。
因此, 映射執行接口得多添加一個方法,用于新增映射對象:
/// <summary>
/// 添加數據映射對象
/// </summary>
void AddDataMapping(DataMapping map);
修改SetCommand方法:
/// <summary>
/// 設置更新命令
/// </summary>
public void SetCommands(DataCommandType commandType, DataSet ds)
{
//設置更新事件時添加映射對象
executeObj.AddDataMapping(this);
}
/// <summary>
/// 更新數據
/// </summary>
public bool Update(DataSet ds)
{
using (conn)
{
if (conn.State == ConnectionState.Closed)
{
conn.Open();
}
IDbTransaction transaction = conn.BeginTransaction(IsolationLevel.ReadCommitted);
foreach (DataMapping map in lisDataMappings)
{
map.SetTransaction(transaction);
}
try
{
foreach (DataMapping map in lisDataMappings)
{
map.Update(ds);
}
transaction.Commit();
}
catch (Exception ex)
{
transaction.Rollback();
throw new System.Exception(ex.Message);
}
}
return true;
}
//DataMapping類設置事務
/// <summary>
/// 設置數據提交事務
/// </summary>
public void SetTransaction(IDbTransaction transaction)
{
if (dataAdapter.InsertCommand != null)
{
dataAdapter.InsertCommand.Transaction = transaction;
}
if (dataAdapter.UpdateCommand != null)
{
dataAdapter.UpdateCommand.Transaction = transaction;
}
if (dataAdapter.DeleteCommand != null)
{
dataAdapter.DeleteCommand.Transaction = transaction;
}
}
到些為止,我們自己的數據訪問層功能已基本完成,但是,我們要如何對其進行調用,現在的方式真能方便的讓我們進行調用嗎?答案是否定的。
暫且至此為止,下一篇我們再進行***的收尾工作,并最終打造成符合自己需求的數據訪問層
原文鏈接:http://www.cnblogs.com/FlySoul/archive/2011/05/04/2036953.html
【編輯推薦】