回顧程序員
上兩篇文章主要講解了我對於數據層的Unit Of Work(工做單元模式)的理解,其中包括了CUD的操做,那麼今天就來談談R吧,文章包括如下幾點:sql
什麼是Query Object框架
Query Object從語義就能看出它的做用,就是將查詢封裝成對象,並在內部轉換成SQL語句或ORM框架的語法來實現查詢操做。this
既然提到了這個模式,確定是有它的好處的,可是光用語言來講明,可能很差體會,那麼就從代碼的角度來看看這個模式的好處吧。編碼
首先,項目數據層接口都會包含根據ID和根據分頁獲取數據的方法,大體代碼以下:spa
IEnumerable<T> FindAll(); IEnumerable<T> FindById<T>(string id); IEnumerable<T> FindByPage<T>(int pageIndex, int pageSize);
而後其餘的業務可能還會提供諸如如下幾種方法,代碼以下:翻譯
IEnumerable<School> FindByName(string name); IEnumerable<School> FindByAge(int age);
光從以上幾個方法就能看出查詢的方式多種多樣,不統一,這樣子在無論在維護仍是在代碼的重用都會相對比較麻煩。orm
可是有了Query Object模式,就能夠得出一個統一的接口,代碼以下:對象
IEnumerable<T> FindBy(Query query); IEnumerable<T> FindBy(Query query, int pageIndex, int pageSize);
光是接口就從原來的4-5個變成了2個,程序員在使用的時候都是一樣的Query Object,而不須要內部是使用SQL仍是NHibernate,又或者EF。接口
研發人員只須要對相應框架實現對應的Query Object翻譯類就能夠輕鬆搞定查詢任務了,那麼事不宜遲,讓咱們一塊兒來分析、實現吧。
基於SQL的實現
首先咱們分析一下查詢條件,如:select * from school where id = '001',條件包含一個名字、值和條件符號,那麼能夠經過定義一個條件符號枚舉和條件類來存儲,代碼以下:
public enum CriteriaOperator { None, Eq, Lt, Gt } public class Criterion { private string m_PropertyName = null; public string PropertyName { get { return m_PropertyName; } } private CriteriaOperator m_Operator = CriteriaOperator.None; public CriteriaOperator Operator { get { return m_Operator; } set { m_Operator = value; } } private object m_Value = null; public object Value { get { return m_Value; } } private Criterion(string propertyName, object value, CriteriaOperator criteriaOperator) { m_PropertyName = propertyName; m_Value = value; m_Operator = criteriaOperator; } public static Criterion Create(string propertyName, object value, CriteriaOperator criteriaOperator) { return new Criterion(propertyName, value, criteriaOperator); } }
接下來須要一個更復雜的條件,如:select * from school where age > 20 and (age < 50 or name = "一中"),查詢中包含了子條件,那麼須要定義額外的枚舉來表示and和or,其次就是查詢內部要能支持子查詢條件,大體代碼以下:
public class Query { private List m_Criterions = new List(); public IEnumerable Criterions { get { return m_Criterions; } } private List m_SubQueries = new List(); public IEnumerable SubQueries { get { return m_SubQueries; } } private QueryOperator m_Operator = QueryOperator.And; public QueryOperator Operator { get { return m_Operator; } } private Query() : this(QueryOperator.And) { } private Query(QueryOperator queryOperator) { m_Operator = queryOperator; } public void Add(Criterion criterion) { m_Criterions.Add(criterion); } public void AddSubQuery(Query subQuery) { m_SubQueries.Add(subQuery); } public static Query Create() { return new Query(); } public static Query Create(QueryOperator queryOperator) { return new Query(queryOperator); } }
咱們從SQL推導出須要實現該功能的類,接下來要從功能類若是翻譯成具體的SQL,回頭看一下Query、Criterion,其實咱們實現的是where後面的SQL語句而已,所以翻譯類能完成的功能就是將Query轉化爲where後面的語句以及包含的參數,大體代碼以下:
private static Tuple<string, List> Translate(Query query, int parameterCount) { List tempSQLs = new List(); List parameters = new List(); if (query.Criterions.Any()) { tempSQLs = query.Criterions.Select(c => { string oper = ""; switch (c.Operator) { case CriteriaOperator.None: return null; case CriteriaOperator.Eq: oper = "="; break; case CriteriaOperator.Lt: oper = "<"; break; case CriteriaOperator.Gt: oper = ">"; break; } string parameterName = string.Format("@param{0}", parameterCount + parameters.Count); parameters.Add(new SqlParameter(parameterName, c.Value)); return string.Format("{0} {1} {2}", c.PropertyName, oper, parameterName); }).Where(sql => !string.IsNullOrEmpty(sql)).ToList(); parameterCount += parameters.Count; } if (query.SubQueries.Any()) { foreach (var subQuery in query.SubQueries) { var subResult = Translate(subQuery, parameterCount); if (subResult == null) continue; tempSQLs.Add(string.Format("({0})", subResult.Item1)); parameters.AddRange(subResult.Item2); parameterCount += subResult.Item2.Count; } } if (tempSQLs.Count == 0) return null; return new Tuple<string, List>( string.Join(query.Operator == QueryOperator.And ? " and " : " or ", tempSQLs), parameters); }
結尾
到這裏咱們就將基於SQL的Query Object模式實現了,不過用起來仍是稍微有點複雜的,這時候你們能夠用Lambda表達式對以上的代碼進行改造,改形成爲以下格式:
var query = Query.Create(); query.Add(s => s.Age > 20 && (s.Age < 50 || s.Name == "一中"));
具體實現的代碼,我就不給出了,畢竟瞭解System.Linq.Expression也是有不少好處的,特別是在項目擴展方面,你們趁此機會鍛鍊一下本身的編碼。
看到這裏相信你們也看到了,對於多表查詢的狀況,Query Object模式對它的支持也是比較困難的,特別是實現起來,嵌套層數太多,所以當SQL存在多表的狀況下,仍是得額外進行實現。
因爲時間的問題,今天就到這裏了,下一篇文章我會繼續Unit Of Work的方式,會基於NHibernate來實現,可能還會加上對於BeGoo的擴展,敬請期待。
再次提醒各位,以上代碼均爲示例代碼,雖然能夠編譯經過,可是最好不要直接應用到項目當中,需根據項目具體狀況進行重構後使用。
若是文章有任何錯誤和問題,歡迎留言,謝謝你們。