打掉重練,n-tier 再續...
最近因為在看辦公室那邊的程式碼,雖然廠商寫的我覺得沒有說很好,但有些方法還是值得我學習
像在上面我學到一些有關 Wcf ,只是這個我本來就有排定要學(為了作個 Service 讓別的學校連資料用...
(用了 Wcf ,用別的語言寫的程式也能快速要資料,像 php 就可以透過 SoapClient 這樣...)
因為我本來就有玩 n-tier ,只是 Linq 實在太好用,結果後面我都是用 Linq 在玩弄資料而已XD
不過最近因為在改寫之前寫的某專案,打算改版那邊的系統,所以又重新在玩... 來個舊瓶裝新酒(!?
因為資料庫的資料不見大概會很困擾,整個資料庫的架構我仍衍用舊的,所以主力都在網頁系統的改寫
那這次一次大改寫,直接把 Wcf 跟 n-tier 給塞進去,那這篇我打算介紹的是我寫的資料庫架構
我資料庫是拆成兩個專案檔的,一個是 WCT.MyData 及 WCT.MyData.SqlServer
所有專案方法的呼叫及回傳的資料結構全在 WCT.MyData 專案中定義,而 WCT.MyData.SqlServer 則是放著 DataAccess 的語法
當然,這兩個其實就分別涵蓋商業邏輯層 (WCT.MyData) 及 資料存取層 (WCT.MyData.SqlServer)
而之後我會再建構一個 Wcf 的服務來當介面層,就是一個三層式架構了 XD
這樣的架構拆的還蠻好的,但未來的事誰也說不準,說不定幾年後,我又想把他拆掉重練(抹汗~),當然,這是題外話(呵~)
在 WCT.MyData 中最主要的核心當然就是很有默契命名成 DbHelper 這類別,透過他實作各種有關資料庫的操作
其中 DbHelper 去 google 一下就能找到很多人的很多種寫法,而我不得不說,我的專案很多的構想其實是來自我以前改 Discuz!NT 的經驗
他是一個很完整的專案,提供我很多想法,但有些東西我根本用不掉也還得踢除,像是他有平衡的機制,但我根本就只有一台 SqlServer ,所以這個就是不必要的東西了
而不一樣的地方是我的 DbHelper 並不會只丟回像 DbDataReader 或 DataTable 等,我有透過一些語法回傳一個完整的類別,所以型態那些都會是明確的
那說這麼多,還是來看一些些相關的程式碼吧,其中 DbHelper 因為太長我就不丟上來了,你可以 google 別人的,大致上都類似(!?
那我的系統在 MyData 那專案檔裡還放置了像資料表結構的定義,其中在上面都有加上 DataContract 定義,本來這個我是想在 Wcf 那邊定義,但後來就甘脆全整併到這邊了,以下是舉例的 Source Code
[DataContract] public class Test { [DataMember] public int id { get; set; } [DataMember] public string Name { get; set; } }
那透過這樣的撰寫就得到一個類似資料表的結構了,另外我有定義一個結構來放置資料的 DbType, dbLength 及 資料的 Type
public class RespositoryFieldType { public DbType dbType { get; set; } public int dbLength { get; set; } public Type type { get; set; } public RespositoryFieldType(string Name, Type type, DbType dbType, int dbLength) { this.type = type; this.dbType = dbType; this.dbLength = dbLength; } }
會需要這樣的結構是我想撰寫一個類似 jqGrid 的 Filter 功能,讓整個類別我可以透過 AddFilter 的方式新增想要 Where 出來的資料,最後再透過 Select 函數將他新增的 Filter 後的結果一次取出來,而藉助
public abstract class RepositoryExtens<T> : IExtraRepository<T> { protected Dictionary<string, RespositoryFieldType> m_FieldType = Utils.GetRespositoryFieldType(typeof(T)); protected Dictionary<string, List<object>> m_Filter = new Dictionary<string, List<object>>(); protected Dictionary<string, WCT.MyData.Enumerations.OrderDirection> m_OrderBy = new Dictionary<string, Enumerations.OrderDirection>(); #region [ Filter 擴展 ] public void AddFilter(string key, object value) { if (!m_FieldType.ContainsKey(key) || !m_FieldType[key].type.Equals(value.GetType())) return; if (m_Filter.ContainsKey(key)) { m_Filter[key].Add(value); } else { m_Filter.Add(key, new List<object>() { value }); } } #endregion #region [ OrderBy 擴展 ] public void AddOrderBy(string key, Enumerations.OrderDirection orderDirection) { if (m_OrderBy.ContainsKey(key)) { m_OrderBy[key] = orderDirection; } else { m_OrderBy.Add(key, orderDirection); } } #endregion }
這是我撰寫出來的抽象類別,透過這類別來實現動態加 Filter 及 Sort 到我的 DataAccess 層,最後他在 Select 時透過呼叫裡面的 m_Filter 字典就知道有哪些要被加到 Where 裡,而透過 m_OrderBy 則能取出要用哪個欄名排序,並且得到排序的幂次
最後透過在 WCT.MyData.SqlServer 專案中我撰寫的 Extensions 來進行 ToWhere 及 OrderBy 的合併,語法如下
public static class SqlExtensions { /// <summary> /// 透過 filter 變數產生 Where 字串 /// </summary> /// <param name="filter"></param> /// <param name="fieldType"></param> /// <param name="varCount"></param> /// <param name="prams"></param> /// <returns></returns> public static string ToWhere(this Dictionary<string, List<object>> filter, ref Dictionary<string, RespositoryFieldType> fieldType, ref int varCount, ref List<DbParameter> prams) { StringBuilder sql = new StringBuilder(); RespositoryFieldType m_fieldType; bool NotFirst = false; if (filter.Count > 0) { sql.Append(" WHERE "); foreach (var kv in filter) { if (kv.Value.Count == 0) continue; m_fieldType = fieldType[kv.Key]; if (NotFirst) { sql.Append(" AND "); } else { NotFirst = true; } if (kv.Value.Count > 1) { sql.AppendFormat("[{0}] In (", kv.Key); for (int i = 0; i < kv.Value.Count; i++) { if (i > 0) sql.Append(","); sql.AppendFormat("@Var{0}", varCount); prams.Add(DbHelper.MakeInParam(string.Format("@Var{0}", varCount), m_fieldType.dbType, m_fieldType.dbLength, kv.Value[i])); varCount++; } sql.Append(")"); } else { sql.AppendFormat("[{0}] = @Var{1}", kv.Key, varCount); prams.Add(DbHelper.MakeInParam(string.Format("@Var{0}", varCount), m_fieldType.dbType, m_fieldType.dbLength, kv.Value[0])); varCount++; } } } return sql.ToString(); } /// <summary> /// 透過 orderBy 變數產生 order by 字串 /// </summary> /// <param name="orderBy"></param> /// <returns></returns> public static string ToOrderBy(this Dictionary<string, WCT.MyData.Enumerations.OrderDirection> orderBy) { StringBuilder sql = new StringBuilder(); bool isFirst = true; if (orderBy.Count > 0) { sql.Append(" ORDER BY "); foreach (var kv in orderBy) { if (!isFirst) sql.Append(","); sql.AppendFormat("[{0}]", kv.Key); if (kv.Value == Enumerations.OrderDirection.Desceding) sql.Append(" DESC"); isFirst = false; } } return sql.ToString(); } }
其實寫了很多 Code 才達到部份 Linq 能達到的功能,但是能自己實作出這樣的程式其實很有成就感
不過如果是很趕的專案還是用 Linq 吧,用 n-tier 只會拉長開發的時程,但是就長遠而言,由於可以把工作切出去,在大型團隊上開發我想還是必要的
只是像我開發的專案很多時候都是一人樂,那 n-tier 其實沒啥幫助(哈~
礙於我不想一開起來就滿滿的文字,其實我的 code 省略了很多,只有放一些比較關鍵的程式碼上來
如果對文章有任何問題也歡迎一起討論!
2014/6/12 隨筆:最近練習了 Expression Tree ,其實這個很容易透過 Expression Tree 作衍伸XD
然後文章請勿轉載,這畢竟是我花了不少時間的心血結晶,謝謝!
留言
張貼留言