日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

QueryBuilder : 打造优雅的Linq To SQL动态查询

發布時間:2024/4/11 数据库 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 QueryBuilder : 打造优雅的Linq To SQL动态查询 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

首先我們來看看日常比較典型的一種查詢Form

這個場景很簡單:就是根據客戶名、訂單日期、負責人來作篩選條件,然后找出符合要求的訂單。

在那遙遠的時代,可能避免不了要寫這樣的簡單接口:

public interface IOrderService {IList<Order> Search(string customer, DateTime dateFrom, DateTime dateTo, int employeeID); }

具體愛怎么實現就怎么實現啦,存儲過程,ORM框架。這里假定是用了孩童時代就開始有的存儲過程吧:

Create Procedure usp_SearchOrder @Customer nVarchar(20), @DateFrom DateTime, @DateTo DateTime, @EmployeeID Int AS /*... 以下省去幾百行SQL語句*/

接著寫一個類OrderService實現IOrderService, 調用以上存儲過程,洋洋灑灑的寫上幾句代碼就可以“安枕睡覺”了。但是,噩夢就從此開始了。

客戶的需求是不斷變化的。過了一段時間,設計這個接口的工程師就先被夸贊一番,然后說客戶提出需要加多“一個”篩選條件。工程師可能也想過這一點,加一個篩選條件“不外乎“給接口加個參數,存儲過程加個參數,where那里加個條件,……苦力活一堆。

客戶的篩選條件是這樣:既然訂單里有“國家”字段,就想根據國家來篩選條件,并且國家可多選,如下圖:

工程師看到圖可能就倒下了……

以上可以當作笑話看看,不過話說回來,沒有一個通用的查詢框架,單靠這樣的接口

public interface IOrderService {IList<Order> Search(string customer, DateTime dateFrom, DateTime dateTo, int employeeID); }

是根本不能適應需求變化。

在沒有Linq 的時代, SQL“ 強人”就試圖通過拼接字符串的方法來結束存儲過程帶來的痛苦,

IList<Order> Search(string sqlQurey);

結果進入另一個被“SQL注入”的時代(注:我大學時也有一段時間玩過“SQL注入”,不亦樂乎,現在基本上很少找到能夠簡單注入的網站了,有磨難就有前進的動力嘛)。

來到Linq To SQL 的時代 (不得不贊嘆Linq把查詢發揮到淋漓盡致), 某些朋友就能輕易地揮灑Linq表達式來解決查詢問題:

IList<Order> Search(Expression<Func<Order, bool>> expression);

查詢語句:

Expression<Func<Order, bool>> expression = c =>c.Customer.ContactName.Contains(txtCustomer.Text) &&c.OrderDate >= DateTime.Parse(txtDateFrom.Text) && c.OrderDate <= DateTime.Parse(txtDateTo.Text) &&c.EmployeeID == int.Parse(ddlEmployee.SelectedValue);

然后再一次 “安枕睡覺”。一覺醒來還是不行。

客戶又來新需求:負責人的下拉框加個“ALL”的選項,如果選了“ALL”就搜索所有的負責人相關的Order。

工程師刷刷幾下,又加if else,又加 and 來拼裝expression;接著又來新需求,…… 最后expression臃腫無比 (當然這個故事是有點夸張)。

為什么用上“先進”的工具還是會倒在慘不忍睹的代碼海洋里呢?因為Microsoft提供給我們的只是“魚竿”。這種魚竿不管在小河還是大海都能釣到東西,而且不管你釣的是鯊魚還是鯨魚,也保證魚竿不會斷。但是有些人能釣到大魚,有些則釣到一雙拖鞋。因為關鍵的魚餌沒用上。也就是說,Microsoft給了我們強大的Linq 表達式,可不是叫我們隨便到表現層一放就了事,封裝才是硬道理。

于是,千呼萬喚始出來,猶抱 QueryBuilder 半遮臉:

var queryBuilder = QueryBuilder.Create<Order>().Like(c => c.Customer.ContactName, txtCustomer.Text).Between(c => c.OrderDate, DateTime.Parse(txtDateFrom.Text), DateTime.Parse(txtDateTo.Text)).Equals(c => c.EmployeeID, int.Parse(ddlEmployee.SelectedValue)).In(c => c.ShipCountry, selectedCountries );

這樣代碼就清爽很多了,邏輯也特別清晰,即使不懂Linq 表達式的人也能明白這些語句是干什么的,因為它的語義基本上跟SQL一樣:

WHERE ([t1].[ContactName] LIKE '%A%') AND ? (([t0].[OrderDate]) >= '1/1/1990 12:00:00 AM') AND (([t0].[OrderDate]) <= '9/25/2009 11:59:59 PM') AND ? (([t0].[EmployeeID]) = 1) AND ? ([t0].[ShipCountry] IN ('Finland', 'USA', 'UK'))

?

對于使用這個QueryBuilder的人來說,他覺得很爽,因為他明白釣什么魚用什么魚餌了,模糊查詢用Like,范圍用Between,……

對于編寫這個QueryBuilder的人來說,也覺得很爽,因為他本身熱愛寫通用型的代碼,就像博客園的老趙那樣。

?

看到使用方式,聰明人自然就已經想到大概的實現方式。就像廚師吃過別人煮的菜,自然心中也略知是怎么煮的。

實現方式并不難,這里簡單說明一下:

QueryBuilder.Create<Order>() 返回的是IQueryBuilder<T> 接口,而IQueryBuilder<T> 接口只有一個 Expression 屬性:

/// <summary> /// 動態查詢條件創建者 /// </summary> /// <typeparam name="T"></typeparam> public interface IQueryBuilder<T> {Expression<Func<T, bool>> Expression { get; set; } }

于是 Like, Between, Equals, In就可以根據這個Expression 來無限擴展了。

以下是實現Like的擴展方法:

/// <summary> /// 建立 Like ( 模糊 ) 查詢條件 /// </summary> /// <typeparam name="T">實體</typeparam> /// <param name="q">動態查詢條件創建者</param> /// <param name="property">屬性</param> /// <param name="value">查詢值</param> /// <returns></returns> public static IQueryBuilder<T> Like<T>(this IQueryBuilder<T> q, Expression<Func<T, string>> property, string value) {value = value.Trim();if (!string.IsNullOrEmpty(value)){var parameter = property.GetParameters();var constant = Expression.Constant("%" + value + "%");MethodCallExpression methodExp = Expression.Call(null, typeof(SqlMethods).GetMethod("Like",new Type[] { typeof(string), typeof(string) }), property.Body, constant);Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(methodExp, parameter);q.Expression = q.Expression.And(lambda);}return q; }

每個方法都是對Expression進行修改,然后返回修改后的Expression,以此實現鏈式編程。

稍微有點意思的就是 In的擴展方法(這個害我費了不少時間,前前后后可能4個小時):

/// <summary> /// 建立 In 查詢條件 /// </summary> /// <typeparam name="T">實體</typeparam> /// <param name="q">動態查詢條件創建者</param> /// <param name="property">屬性</param> /// <param name="valuse">查詢值</param> /// <returns></returns> public static IQueryBuilder<T> In<T,P>(this IQueryBuilder<T> q, Expression<Func<T, P>> property, params P[] values) {if (values != null && values.Length > 0){var parameter = property.GetParameters();var constant = Expression.Constant(values);Type type = typeof(P);Expression nonNullProperty = property.Body;//如果是Nullable<X>類型,則轉化成X類型if (IsNullableType(type)){type = GetNonNullableType(type);nonNullProperty = Expression.Convert(property.Body, type);}Expression<Func<P[], P, bool>> InExpression = (list, el) => list.Contains(el);var methodExp = InExpression;var invoke = Expression.Invoke(methodExp, constant, property.Body);Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(invoke, parameter);q.Expression = q.Expression.And(lambda);}return q; }

?

如果有興趣的朋友可以在文章末下載源代碼,看看其他兩個擴展方法。

嗯,似乎又是時候退場了。什么?你說只有Like, Between, Equals, In不夠用?哦,可以自己擴展IQueryBuilder,自己動手豐衣足食嘛。

我后來又為另外一個Project做了一個“奇怪”的擴展:

譬如,我們知道打印設置里可以直接寫頁數號碼來篩選要打印哪幾頁——1,4,9 或者 1-8 這樣的方式。

于是引發這樣的需求:

左圖查詢Bruce和Jeffz的訂單;右圖查詢B直到Z的客戶訂單。

還美其名曰:Fuzzy ,意即:模糊不清的 (點這里看 Fuzzy詳細意思)

總結:

其實這篇文章已經醞釀好久了,近期工作收獲很多編程技巧。一個Project下來了,又是時候總結一下,希望有空能夠繼續與大家分享。

源代碼下載:CoolCode.Linq.rar

建議參考新一篇《QueryBuilder : 打造優雅的Linq To SQL動態查詢(支持EF、.Net4)

參考:

http://www.cnblogs.com/neuhawk/archive/2007/07/07/809585.html

http://www.cnblogs.com/billgan/archive/2009/01/08/1371809.html

http://www.cnblogs.com/lyj/archive/2008/03/25/1122157.html

http://www.cnblogs.com/126/archive/2007/09/09/887723.html

超強干貨來襲 云風專訪:近40年碼齡,通宵達旦的技術人生

總結

以上是生活随笔為你收集整理的QueryBuilder : 打造优雅的Linq To SQL动态查询的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。