c# 經驗談:巧用Expression運算式 解決類似於sql中 select in 的查詢(適合於中小型項目)

來源:互聯網
上載者:User

我們在項目經常會碰到一些特殊需求 例如下拉框是複選的,查詢條件是根據下拉框中複選項進行拼接

看到此圖後大家肯定會說,這很簡單嘛

將所有的選項 拼成“'1-3','5-9'”  然後放到 in 的字句後面,一查就出來了。

這樣做的確在邏輯上沒有問題,可是大家有沒有想過這個問題,過度的和業務耦合雖然能夠解決

現在的需求但是卻犧牲了代碼優雅和可維護性

 

其實本文的目的是想利用Expression運算式在linq查詢中實現一個優雅的解決方案,

同時也會給大家一個用Expression去拼接sql的思路

 

先上代碼

      public static Expression<Func<T, bool>> GetConditionExpression<T>(string[] options, string fieldName)
{
ParameterExpression left = Expression.Parameter(typeof(T), "c");//c=>
Expression expression = Expression.Constant(false);
foreach (var optionName in options)
{
Expression right = Expression.Call
(
Expression.Property(left, typeof(T).GetProperty(fieldName)), //c.DataSourceName
typeof(string).GetMethod("Contains", new Type[] { typeof(string) }),// 反射使用.Contains()方法
                         s Expression.Constant(optionName)           // .Contains(optionName)
);
expression = Expression.Or(right, expression);//c.DataSourceName.contain("") || c.DataSourceName.contain("")
}
Expression<Func<T, bool>> finalExpression
= Expression.Lambda<Func<T, bool>>(expression, new ParameterExpression[] { left });
return finalExpression;
}

我想用逆推的方式說明下這段代碼,其實我們查詢的目的要實現這樣的效果 , someList.where(c=>c.Name.contains("someName")||c.Name.Contains("someName")||...)

1. 首先我們要確定返回什麼樣的運算式,根據經驗.where後面是需要一個Expression<Func<T, bool>> 這樣的一個運算式,所以方法的傳回型別已經能確定下來了

2. 接下來的任務是拼接類似於c=>c.Name.Contains("") 這樣的運算式,按照自左向右的原則,左側運算式參數c很好理解 就是T,那麼這個運算式的參數也就搞定了,

可以用Expression.Parameter方法來實現,該方法目的是將類型反射並且映射給運算式中的匿名變數 “c” (也可以理解成將參數常量封裝成運算式)

3. 接著是運算式右側的拼接

 再次仔細看下這段代碼

Expression right = Expression.Call 

 (                          

     Expression.Property(left, typeof(T).GetProperty(fieldName)),  //c.DataSourceName     首先是反射擷取c的一個屬性                

     typeof(string).GetMethod("Contains", new Type[] { typeof(string) }),// 聲明一個string.Contains的方法     c.DataSourceName.Contains()                反射使用.Contains()方法  

     Expression.Constant(optionName)           //  c.DataSourceName.Contains(optionName)               封裝常量       

 );

  為什麼要使用Expression.Call ?

(因為c.Name.contains 屬於string.contains()這個方法所以我們必須將該方法封裝成運算式,Expression.Call的功能就是將方法封裝成運算式)

 這時候大家會問contains什麼呢? 當然常量option雖然是string類型,但是仍需封裝成運算式,Expression.Constant(optionName) 起到了封裝常量的作用

 於是c=>c.屬性.Contains(常量) 這個運算式搞定,可是還是有問題:怎麼加上“||” ,聰明的你已經有了答案,Expression.Or()

 

4 最後一步當然非常關鍵,就像產品需要通過流水線進行封裝組合,運算式也不例外:

Expression.Lambda<Func<T, bool>>(expression, new ParameterExpression[] { left });

對於整個運算式來說,左側是參數運算式(ParameterExpression),Expression.Lambda就是=>符號,就右側運算式和參數運算式通過lambda符號進行組合,搞定

 

這樣的話,你只需傳入一個字串數組就能在Linq中實作類別似於sql中select in 的效果了,

很多朋友肯定會問,既然能夠用自訂運算式搞定,那麼可不可以將運算式的思路用於拼接sql?

答案是肯定的。但是如果商務邏輯非常複雜,而且難以把握,還是建議用ado 配合存過實現

 

 

感謝女友一直陪到深夜,讓我堅持把這篇博文寫完,文中如有錯誤,也請大家海涵並且能夠及時告訴我,謝謝!

 

 

 

 

 

 

 

 

 

 

 

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.