標籤:des winform style blog http color
發布一個自己寫的一個輕量級ORM架構,本架構設計期初基於三層架構.所以從命名上來看,瞭解三層的朋友會很好理解.
設計該架構的目的:不想重複的寫增刪改查,把精力放到功能實現上.
發布改架構的原因:希望給初學者一個參考,希望能給予好的建議,給自己一個展示機會.
在我開始之前,先說明一下,我對"軟體工程學"概念東西幾乎不通,最高文化程度:初二,所以不喜勿噴.
開始我的orm設計最底層
最底層的是一個DalBase,它是一個抽象的,實現了增刪改查的基本操作.
它既然是一個抽象的,那麼它的內部就不應該有任何具體成員.
它內部核心對象有:訪問資料庫的對象,產生sql文的對象的抽象定義,建立sql參數的抽象方法,where參數化查詢對象的抽象定義.
/// <summary> /// 訪問資料的DBHelper對象 /// </summary> public abstract DbHelperBase DBHelper { get; } //子類實現 /// <summary> /// 獲得產生sql文本的對象 /// </summary> protected internal abstract BuildSQL BuildSQLTextObj { get; } /// <summary> /// 獲得資料參數對象 /// </summary> protected internal abstract DbParameter GetDbParam(string paramName, object paramValue); /// <summary> /// 建立WhereHelper對象 /// </summary> internal abstract WhereHelper CreateWhereHelper();
如需擴充支援的資料庫種類,只需要分別對這幾個抽象對象進行具體代碼的實現.
以下代碼是增刪改(查詢相對來說比較複雜,單獨放出來)
/// <summary> /// 執行sql語句返回所影響行數 /// </summary> public virtual int ExecuteBySQL(string sqlText, Dictionary<string, object> dbParams) { //執行sql語句的操作都由此方法實現 DbParameter[] parameters = GetDbParam(dbParams); int rows = DBHelper.ExecNonQuery(sqlText, parameters); return rows; } /// <summary> /// 新增1條或多條 /// </summary> public virtual int Add<TModel>(params TModel[] models) where TModel : ModelBase, new() { ThrowModelIsNullException(models); string sqlText; Dictionary<string, object> dbParams;
//這句話其實內部實現訪問了 BuildSQLObj 的 InsertSQLTExtAndParam ,它產生sql語句和sql參數對象,把它又寫成方法是為了讓子類還可以對它進行重寫,這個或許之前想多了,直接重寫Add也是可以的. GenerateInsertSQLTextAndParam(out sqlText, out dbParams, models); int rows = ExecuteBySQL(sqlText, dbParams); return rows; } /// <summary> /// 更新一條或多條記錄,根據sql條件,指定更新欄位(sqlWhere為空白,則按主鍵更新) /// </summary> public virtual int Update<TModel>(string[] fields, string sqlWhere, Dictionary<string, object> dbParams, params TModel[] models) where TModel : ModelBase, new() { ThrowModelIsNullException(models); string sqlText; GenerateUpdateSQLTextAndParam(out sqlText, ref dbParams, sqlWhere, fields, models); int rows = ExecuteBySQL(sqlText, dbParams); return rows; } /// <summary> /// 更新一條或多條記錄,根據sql條件,指定忽略更新的欄位 /// </summary> public virtual int UpdateByIgnoreField<TModel>(string[] ignoreFields, string sqlWhere, Dictionary<string, object> dbParams, params TModel[] models) where TModel : ModelBase, new() { string[] allFields = BuildSQLTextObj.GetAllFields<TModel>(); string[] updateFields = BuildSQLTextObj.RemoveFields(allFields, ignoreFields); return Update(updateFields, sqlWhere, dbParams, models); } /// <summary> /// 根據主鍵刪除記錄 /// </summary> public virtual int Delete<TModel>(params object[] pkValues) where TModel : ModelBase, new() { string sqlText; Dictionary<string, object> dbParams; if (pkValues == null || pkValues.Length < 0) { throw new DbDataException("刪除操作時主鍵為空白!"); } GenerateDeleteSQLTextAndParam<TModel>(out sqlText, out dbParams, pkValues); int rows = ExecuteBySQL(sqlText, dbParams); return rows; }
查詢提供了,3中方法,返回DataSet,返回List,返回DataReader,以及分頁的方法
public virtual DataTable GetDataTable<TModel>(string sqlWhere, Dictionary<string, object> dbParams, string[] orderFields, bool isDesc, params string[] selectFields) where TModel : ModelBase, new(){ string sqlText; GenerateSearchSQLTextAndParam<TModel>(sqlWhere, dbParams, orderFields, isDesc, out sqlText, selectFields); return GetDataTableBySQL(sqlText, dbParams);}public virtual DbDataReader GetDataReader<TModel>(string sqlWhere, Dictionary<string, object> dbParams, string[] orderFields, bool isDesc, params string[] selectFields) where TModel : ModelBase, new(){ string sqlText; GenerateSearchSQLTextAndParam<TModel>(sqlWhere, dbParams, orderFields, isDesc, out sqlText, selectFields); return GetDataReaderBySQL(sqlText, dbParams);}public virtual List<TModel> GetList<TModel>(string sqlWhere, Dictionary<string, object> dbParams, string[] orderFields, bool isDesc, params string[] selectFields) where TModel : ModelBase, new(){ DbDataReader dbReader = GetDataReader<TModel>(sqlWhere, dbParams, orderFields, isDesc, selectFields); List<TModel> list = GetListByDataReader<TModel>(dbReader); return list;}
為什麼有個DataReader方法呢,返回它有兩個用處,1是把它轉換成List,2因為DataSet的擷取內部也是調用了DataReader方法,(通過反編譯可以看到的)
因為DataReader 轉換 List 要比 DataTable to List的效率要快;
DalBase的的準系統其實就差不多了,下邊來介紹下BLLbase的實現,BLLBase主要實現了dal方法的一些重載而已,
從字面意思來說,商務邏輯的基類,我這裡定義了商務邏輯的規則,比如Insert前(後)的事件,查詢前的事件,等等..
BLLBase裡增刪改其實和DalBase並無特別差別,就不介紹了.
主要說下Get的方法.
public virtual DataTable GetDataTable<TModel>(string[] selectFields, string[] orderFields, bool isDesc, WhereHelper dbWhereModel) where TModel : ModelBase, new() { StringBuilder sqlWhere = null; Dictionary<string, object> dbParam = null; GetSqlWhereParam(ref sqlWhere, ref dbParam, dbWhereModel); return GetDataTable<TModel>(sqlWhere.ToString(), dbParam, orderFields, isDesc, selectFields); }
這是一個帶where條件的查詢,返回datatable,其它擷取List,DataReader,方法都是一樣的,WhereHelper這個類的建立,我自豪了很久,在下面將調用時會舉例它的使用及實現.
舉個測試例子:
1.建立一個WinForm程式,引用 ZhCun.Framework.Common 和ZhCunFramework.DataAccess
2.建立Models檔案夾,分別建Test1.cs和Test2.cs ,這兩個是表的映射.如下:
namespace ZhCun.Framework.WinTest.Models{ public class Test1 : ModelBase { [ModelAttribute(IsPrimaryKey = true, IsIdentity = true)] public int Id { set; get; } public string Name { set; get; } public string Age { set; get; } public string Remark { set; get; } }}
映射的Model必須繼承ModelBase,這就是為什麼在DalBase裡面加泛型約束的原因,其實ModelBase我並沒有想好用它來實現什麼,就當個限制條件吧.
另外 ModelAttribute 特性,指定該屬性的映射資料庫表的類型及其它規則,這裡Id表示是一個自增長的主鍵.
3.建立一個BLLCommon 類,這個類名或許叫什麼 XXXXServer ,或許更好一些,它繼承了BLLBase並指定了連接字串.
那兩個表就手工建一下吧,連接字串可以直接指定寫死嘍
public class BLLCommon : BLLBase { static string ConnStr { get { // "Data Source=192.168.0.55;Initial Catalog=aa;uid=sa;pwd=123"; return Configurations.GetConnectString("Test"); } } public BLLCommon() : base(DatabaseTypeEnum.SQLServer, ConnStr) { }}
BLLCommon指定了連接字串和資料庫類型,如果一個項目使用多個(或多種)資料庫,可以建立多個BLLCommon,
BLLBase的所有方法都定義的虛方法,所以在這裡可以重寫你要改的東西,來完成商務邏輯的限制或約束.
如,我想要在增加Test1表資料時,Remark賦值‘aa‘
public override int Add<TModel>(params TModel[] models) { foreach (var item in models) { Test1 m = item as Test1; if (m != null) { m.Remark = "aa"; } } return base.Add<TModel>(models); }
下面是調用代碼:
此代碼實現了,新增和更新,及事務的使用方法.
BLLCommon _BllObj = new BLLCommon(); private void btnAdd_Click(object sender, EventArgs e) { Test1 t1 = new Test1(); Test2 t2 = new Test2(); t1.Name = txtName.Text; t1.Age = txtAge.Text; t1.Remark = txtRemark.Text; t2.Name = txtName.Text; t2.Age = txtAge.Text; t2.Remark = txtRemark.Text; try { _BllObj.TransStart(); _BllObj.Add(t2); _BllObj.Add(t1); var model = _BllObj.GetModel<Test1>(1); if (model != null) { model.Remark = "更新時間:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); _BllObj.Update(new string[] { "Remark" }, model); } _BllObj.TransCommit(); MessageBox.Show("提交成功!"); } catch (Exception ex) { _BllObj.TransRollback(); MessageBox.Show(ex.Message); } }
帶where查詢的方法調用:
WhereHelper wh1 = _BllObj.CreateWhereHelper(); wh1.Add("Name").Equal("張三"); List<Test1> list1 = _BllObj.GetList<Test1>(wh1);
WhereHelper對象為啥要用BLLObj來建立,這個解釋一下,考慮到不同資料庫的查詢應該有不同的文法,把WhereHelper抽象出來,也是為了擴充.
引用BLLBase的時候指定了資料庫,所以BLL是知道建立哪中資料庫的WhereHelper的,所以用BLL對象來建立WhereHelper是最合適的,這樣如果切換資料庫不會受任何影響.
wh1.Add("欄位1") .Equal(1) .And("欄位2") .EqualNot(2);
這個是收到jquery的啟發,來設計的類,每一個操作都返回了當前對象(即WhereHelper),也就是說,它可以無限的"點"下去.
使用它的最大好處是:你不用考慮參數名的重複,不用考慮換庫的相容性,操作起來是如此簡單.
關於WHereHelper的使用及設計思想請看: 輕量級ORM架構 之 WhereHelper (二)
費勁的寫了這麼多,如果有人看有人有要求,或有什麼建議,請留言.
昨天看了一篇關於程式員的文章,一個優秀的程式員6大特質,其中之一是:懂的分享.
把這套架構源碼共用下 下載
下載說明:
本架構沒有用於過任何商業用途(當然以後就不一定了)
歡迎 指正,批評,最佳化,建議,抄襲及商業使用
共用的目的是為了最佳化一下架構,接收一下建議,瞭解下不足.