1. 程式人生 > >C#進階系列——動態Lamada

C#進階系列——動態Lamada

前言:在DDD系列文章裡面,我們在後臺倉儲裡面封裝了傳遞Lamada表示式的通用方法,類似這樣:

     public virtual IQueryable<TEntity> Find(Expression<Func<TEntity, bool>> express)
        {
            Func<TEntity, bool> lamada = express.Compile();
            return UnitOfWork.context.Set<TEntity>().Where(lamada).AsQueryable<TEntity>();
        }

通過前端傳過來的Lamada表示式,直接放到Where條件裡面查詢。那麼問題來了,我們前端如何傳入Lamada呢?當然,有人說了,這個不用傳啊,前端直接.Find(x=>x.Name=="abc")這樣寫就好了啊。確實,如果前端條件只有一個條件,你確實可以這樣簡單處理,但是實際開發的過程中,我們很多時候是需要傳遞多個引數,並且.Find(x=>x.Name=="abc")這種寫法也不利於方法的封裝。於是,我們神奇的動態Lamada誕生了。

一、再談Lamada表示式

1、匿名委託

之前在介紹委託的時候我們介紹過一種特殊的匿名委託,它型如:

    class Program
    {
        
private delegate void SayHello(string name); static void Main(string[] args) { Say("張三", delegate(string name) { Console.WriteLine("你好," + name); }); Say("Zhangsan", delegate(string name) { Console.WriteLine(
"Hello," + name); }); } static void Say(string name,SayHello dTest) { dTest(name); } }

也就是說,不用定義一種具體的委託方法去對應SayHello(string name);,而直接delegate(string name){}這樣定義一種匿名的委託去執行,這樣能減少部分定義具體方法的程式碼。

2、Lamada表示式進化史

瞭解了匿名委託的概念,我們來看看我們經常使用的Linq裡面的擴充套件方法Where、Select等。先來看看一般用法:

var lstTest = new List<string>();
//.......業務邏輯
var lstRes = lstTest.Where(x => x.Contains("_"));

我們來將Where裡面的x => x.Contains("_")分解。

初級進化(最原始的匿名委託形式):

Func<string, bool> oFunc = delegate(string x) { return x.Contains("_"); };
lstRes = lstTest.Where(oFunc);

高階進化(型如Lamada,但還有匿名委託的影子):

Func<string, bool> oFunc = (string x) => { return x.Contains("_"); };
lstRes = lstTest.Where(oFunc);

究極進化(完完全全的Lamada)

Func<string, bool> oFunc = x => x.Contains("_");
 lstRes = lstTest.Where(oFunc);

有沒有很強大,是不是一樣一樣的。其實這樣一看lamada就是匿名委託的縮略形式。x => x.Contains("_")表示式左邊的表示Func裡面的string型別變數,x.Contains("_")表示bool型別的返回值。有了這份進化史,程式設計師再也不用擔心面試官問我Lamada怎麼回事了。

二、動態Lamada

與其叫動態Lamada,更加嚴謹一點應該叫動態Expression,因為拼接Lamada表示式用的基本都是Expression的類和方法。博主習慣,暫且就叫它動態Lamada吧。廢話不多說,直接吃點栗子吧。

  public class DTO_ORDER
    {
        public string TO_ORDER_ID { get; set; }
        public string ORDER_NO { get; set; }
        public string ORDER_NAME { get; set; }
        public int ORDER_STATUS {get;set;}
    }
    static void Main()
        {
            //1.定義lamada的引數,型如我們常寫的“x=>”
            ParameterExpression m_Parameter = Expression.Parameter(typeof(DTO_ORDER), "x");

            //2.定義要使用lamada的屬性成員(比如我們這裡要對DTO_ORDER物件的ORDER_NO屬性做篩選)
            MemberExpression member = Expression.PropertyOrField(m_Parameter, "ORDER_NO");

            //3.定義篩選的操作(是大於、等於、小於、like等)
            Expression expRes = Expression.Equal(member, Expression.Constant("aaaa", member.Type));

            //4.將表示式轉換為Lamada的表示式
            Expression<Func<DTO_ORDER, bool>> exprelamada = Expression.Lambda<Func<DTO_ORDER, bool>>(expRes, m_Parameter);

            var lstRes = new List<DTO_ORDER>();
            for (var i = 0; i < 10; i++)
            {
                var oModel = new DTO_ORDER();
                oModel.ORDER_NO = i % 2 == 0 ? "aaaa" : "bbbb";
                lstRes.Add(oModel);
            }
            //5.將Expression表示式轉換為Func委託,用於Where裡面的引數
            var lamada = exprelamada.Compile();
            lstRes = lstRes.Where(lamada).ToList();
    }

以上就構造了一個查詢List<DTO_ORDER>物件裡面ORDER_NO 屬性等於aaaa的lamada表示式。我們看看執行效果截圖:

是不是已經得到了我們想要的表示式!有沒有很簡單。。。

三、動態Lamada的使用

看到這裡有人就鬱悶了,為了得到x=>x.ORDER_NO=="aaaa"這種表示式,你繞了這麼大一圈,有什麼屌用?直接lstRes=lstRes.Where(x=>x.ORDER_NO=="aaaa");就能夠搞定的事,你非要把簡單問題複雜化。其實不然,有一定程式設計經驗的朋友肯定知道,一般我們前端傳過來的查詢引數肯定不會只有一個,當需要查詢多個引數時需要我們構造一個統一的Lamada傳遞到後臺;當然你也可以說,我將多個引數全部傳遞到後臺,然後再後臺使用IQueryable介面去過濾。當然,這確實可行,但是別忘了我們封裝Find(Expression exp...)的意義,不就是為了簡化方法麼,從這點來說,構造動態Lamada非常必要。

1、通用Lamada表示式類

博主封裝了一個簡單操作(大於、等於、小於、like等)的動態Lamada類。

  public class LamadaExtention<Dto> where Dto:new ()
    {
        private List<Expression> m_lstExpression = null;
        private ParameterExpression m_Parameter = null;

        public LamadaExtention()
        {
            m_lstExpression = new List<Expression>();
            m_Parameter = Expression.Parameter(typeof(Dto), "x");
        }
     //構造表示式,存放到m_lstExpression集合裡面
public void GetExpression(string strPropertyName, object strValue, ExpressionType expressType) { Expression expRes = null; MemberExpression member = Expression.PropertyOrField(m_Parameter, strPropertyName); if (expressType == ExpressionType.Contains) { expRes = Expression.Call(member, typeof(string).GetMethod("Contains"), Expression.Constant(strValue)); } else if (expressType == ExpressionType.Equal) { expRes = Expression.Equal(member, Expression.Constant(strValue, member.Type)); } else if (expressType == ExpressionType.LessThan) { expRes = Expression.LessThan(member, Expression.Constant(strValue, member.Type)); } else if (expressType == ExpressionType.LessThanOrEqual) { expRes = Expression.LessThanOrEqual(member, Expression.Constant(strValue, member.Type)); } else if (expressType == ExpressionType.GreaterThan) { expRes = Expression.GreaterThan(member, Expression.Constant(strValue, member.Type)); } else if (expressType == ExpressionType.GreaterThanOrEqual) { expRes = Expression.GreaterThanOrEqual(member, Expression.Constant(strValue, member.Type)); } //return expRes; m_lstExpression.Add(expRes); } //針對Or條件的表示式 public void GetExpression(string strPropertyName, List<object> lstValue) { Expression expRes = null; MemberExpression member = Expression.PropertyOrField(m_Parameter, strPropertyName); foreach (var oValue in lstValue) { if (expRes == null) { expRes = Expression.Equal(member, Expression.Constant(oValue, member.Type)); } else { expRes = Expression.Or(expRes, Expression.Equal(member, Expression.Constant(oValue, member.Type))); } } m_lstExpression.Add(expRes); }
     //得到Lamada表示式的Expression物件
public Expression<Func<Dto, bool>> GetLambda() { Expression whereExpr = null; foreach (var expr in this.m_lstExpression) { if (whereExpr == null) whereExpr = expr; else whereExpr = Expression.And(whereExpr, expr); } if (whereExpr == null) return null; return Expression.Lambda<Func<Dto, Boolean>>(whereExpr, m_Parameter); } }
   //用於區分操作的列舉
public enum ExpressionType { Contains,//like Equal,//等於 LessThan,//小於 LessThanOrEqual,//小於等於 GreaterThan,//大於 GreaterThanOrEqual//大於等於 }

2、使用場景

博主專案中有某一個頁面,查詢條件非常多,需要傳遞到後臺很多引數。先來看看頁面:

 來看後臺web api程式碼

public object Get(int limit, int offset, string strBodyno, string strVin, string strOrderno, string strEngincode,
                  string strOrderstatus, string strTranscode, string strVms, string strCarcode,
                  string strImportStartdate, string strImportEnddate, string strSendStartdate, string strSendEnddate)
        {
            //1.定義物件,傳入泛型
            var oLamadaExtention = new LamadaExtention<DTO_TO_ORDER>();

            //2.依次構造Lamada表示式
            if (!string.IsNullOrEmpty(strBodyno))
            {
                oLamadaExtention.GetExpression("BODY_NO", strBodyno, ExpressionType.Contains);
            }
            if (!string.IsNullOrEmpty(strVin))
            {
                oLamadaExtention.GetExpression("VIN", strVin, ExpressionType.Contains);
            }
            if (!string.IsNullOrEmpty(strOrderno))
            {
                oLamadaExtention.GetExpression("ORDER_NO", strOrderno, ExpressionType.Contains);
            }
            if (!string.IsNullOrEmpty(strEngincode))
            {
                oLamadaExtention.GetExpression("ENGIN_CODE", strEngincode, ExpressionType.Contains);
            }
            if (!string.IsNullOrEmpty(strOrderstatus))
            {
                if (strOrderstatus.Contains(","))
                {
                    var lstValue = strOrderstatus.Split(",".ToCharArray(), StringSplitOptions.RemoveEmptyEntries).ToList();
                    var lstObj = new List<object>();
                    lstValue.ForEach(x => {
                        lstObj.Add(Convert.ToInt16(x));
                    });
                    oLamadaExtention.GetExpression("ORDER_STATUS", lstObj);
                }
                else
                {
                    oLamadaExtention.GetExpression("ORDER_STATUS", Convert.ToInt16(strOrderstatus), ExpressionType.Equal);
                }
            }
            if (!string.IsNullOrEmpty(strTranscode))
            {
                oLamadaExtention.GetExpression("TRANS_CODE", strTranscode, ExpressionType.Contains);
            }
            if (!string.IsNullOrEmpty(strVms))
            {
                oLamadaExtention.GetExpression("VMS_NO", strVms, ExpressionType.Contains);
            }
            if (!string.IsNullOrEmpty(strCarcode))
            {
                oLamadaExtention.GetExpression("TM_MODEL_MATERIAL_ID", strCarcode, ExpressionType.Contains);
            }

            if (!string.IsNullOrEmpty(strImportStartdate))
            {
                oLamadaExtention.GetExpression("CREATE_DATE", Convert.ToDateTime(strImportStartdate), ExpressionType.GreaterThanOrEqual);
            }
            if (!string.IsNullOrEmpty(strImportEnddate))
            {
                oLamadaExtention.GetExpression("CREATE_DATE", Convert.ToDateTime(strImportEnddate), ExpressionType.LessThanOrEqual);
            }
            if (!string.IsNullOrEmpty(strSendStartdate))
            {
                oLamadaExtention.GetExpression("OFFLINE_DATE_ACT", Convert.ToDateTime(strSendStartdate), ExpressionType.GreaterThanOrEqual);
            }
            if (!string.IsNullOrEmpty(strSendEnddate))
            {
                oLamadaExtention.GetExpression("OFFLINE_DATE_ACT", Convert.ToDateTime(strSendEnddate), ExpressionType.LessThanOrEqual);
            }
            //3.得到需要的Lamada表示式Expression
            var lamada = oLamadaExtention.GetLambda();
            var lstRes = orderManager.Find(lamada).ToList();
            
            //4.得到Bootstrap Table需要的物件
            var oRes = new PageRowData();
        return oRes; ;
        }

倉儲基類裡面的find方法:

     public virtual IQueryable<TEntity> Find(Expression<Func<TEntity, bool>> express)
        {
            Func<TEntity, bool> lamada = express.Compile();
            return UnitOfWork.context.Set<TEntity>().Where(lamada).AsQueryable<TEntity>();
        }

四、小結

至此,所謂的動態Lamada就完了。如果你之前用過,請一笑而過;當然如果你沒用過,學習點新東西也是好的。請不要嘲笑博主亂下定義,叫動態Lamada挺好的呢。當然你可以叫動態Expression,動態Linq都行,不管叫什麼,正確使用才是王道。

相關推薦

C#系列——動態Lamada

前言:在DDD系列文章裡面,我們在後臺倉儲裡面封裝了傳遞Lamada表示式的通用方法,類似這樣:      public virtual IQueryable<TEntity> Find(Expression<Func<TEntity, bool>> express)

C#系列——動態Lamada(二:優化)

前言:前幾天寫了一篇動態Lamada的文章C#進階系列——動態Lamada,受園友的啟發,今天打算來重新優化下這個動態Lamada的工具類。在此做個筆記,以免以後忘了。 一、原理分析 上篇裡面我們說了動態Lamada的使用必要性以及使用場景,但是感覺用在專案裡面還不太方便,最難用的就是需要傳遞屬性名稱的字

【6】C++系列動態記憶體分配)

問題:之前在寫程式的時候計劃好我們需要哪些資料,都定義好,但是有些時候我們並不知道我要處理的程式規模有多大,也不知道陣列開多大合適,是儘量大?分配了太大空間可能會造成記憶體的浪費。只有在程式真正執行起來才會知道這次執行要處理的資料規模有多大——那就有人想,能不能用變數來確定陣

C#系列——WebApi 異常處理解決方案(轉)

機制 輸出 ges 如果 但是 rom lba slist 解決 出處:http://www.cnblogs.com/landeanfen/p/5363846.html 閱讀目錄 一、使用異常篩選器捕獲所有異常 二、HttpResponseException自

C#系列——WebApi 接口測試工具:WebApiTestClient

spa type 區域 all 手動 shee 找到 網絡 打開文件 C#進階系列——WebApi 接口測試工具:WebApiTestClient 前言:這兩天在整WebApi的服務,由於調用方是Android客戶端,Android開發人員也不懂C#語法,API裏

C#系列——WebApi 路由機制剖析:你準備好了嗎?

事先 blank path can tex 全局配置 dex 找不到 save 前言:從MVC到WebApi,路由機制一直是伴隨著這些技術的一個重要組成部分。 它可以很簡單:如果你僅僅只需要會用一些簡單的路由,如/Home/Index,那麽你只需要配置一個默認路由就能簡

C#系列——WebApi 跨域問題解決方案:CORS

dea ati ice pro target default 異常 測試工具 復雜 前言:上篇總結了下WebApi的接口測試工具的使用,這篇接著來看看WebAPI的另一個常見問題:跨域問題。本篇主要從實例的角度分享下CORS解決跨域問題一些細節。 WebApi系列文章

[轉]C#系列——WebApi 接口返回值不困惑:返回值類型詳解

try 接口測試工具 des rep home creat port 調用 學習 本文轉自:http://www.cnblogs.com/landeanfen/p/5501487.html 閱讀目錄 一、void無返回值 二、IHttpActionResult

C#系列——WebApi 身份認證解決方案:Basic基礎認證

str 常見 bre 這一 dex ace timeout ticket 結合 閱讀目錄 一、為什麽需要身份認證 二、Basic基礎認證的原理解析 1、常見的認證方式 2、Basic基礎認證原理 三、Basic基礎認證的代碼示例 1、登錄過程 2、/Home/I

C#系列——WebApi 接口參數不再困惑:傳參詳解

pub 博客 bapi write ids 簡單 指定 數組 這也 https://www.cnblogs.com/landeanfen/p/5337072.html 閱讀目錄 一、get請求 1、基礎類型參數 2、實體作為參數 3、數組作為參數 4

【8】C++系列(過載)

1、過載規則 c++幾乎可以過載全部的運算子,而且只能夠過載c++已有的運算子。 其中,不能過載的運算子:"." 、 ".*" 、"::"、"?:" 過載之後運算子的優先順序和結合性都不會改變。 運算子過載是針對新型資料的實際需要,對原有運算子進行適當的改造。例如: 使複數的物件

【7】C++系列(類的繼承與派生)

1、繼承的概念 繼承:在儲存原有類的屬性和功能的基礎上,擴充套件新的功能。 開發類庫的團隊和使用類庫的團隊很可能不是一個,有些東西是不能訪問的。 繼承和派生是同一個問題的不同視角: 保持已有類的特性而構建新類的過程成為繼承;在已有類的基礎上新增自己的特性而產生新類的過程叫做派生。

【5】C++系列(陣列和指標2)

1、實驗 實驗1:3 x 3矩陣的轉置 #include<iostream> using namespace std; void swap(int &a, int &b) { int temp = a; a = b; b = temp;

【9】C++系列(泛型設計以及STL標準模板庫)

1、泛型程式設計基本概念 泛型程式設計:編寫不依賴與具體資料型別的程式,將演算法從特定的資料結構中抽象出來,成為通用的。C++的模板為泛型程式設計定義了關鍵的基礎。 兩個術語:概念,模型 概念:用來界定具備一定功能的資料型別,例如:將“可以比較大小的所有資料型別(有比較

【轉】C#系列——WebApi 介面引數不再困惑:傳參詳解

正文 前言:還記得剛使用WebApi那會兒,被它的傳參機制折騰了好久,查閱了半天資料。如今,使用WebApi也有段時間了,今天就記錄下API介面傳參的一些方式方法,算是一個筆記,也希望能幫初學者少走彎路。本篇針對初初使用WebApi的同學們,比較基礎,有興趣的且看看。 WebApi系列文章

C#系列——WebApi 介面返回值不困惑:返回值型別詳解

前言:已經有一個月沒寫點什麼了,感覺心裡空落落的。今天再來篇乾貨,想要學習Webapi的園友們速速動起來,跟著博主一起來學習吧。之前分享過一篇 C#進階系列——WebApi介面傳參不再困惑:傳參詳解 ,這篇博文內容本身很基礎,沒想到引起很多園友關注,感謝大家的支援。作為程式猿,我們都知道引數和返回值是程式設計

C#系列——WebApi 異常處理解決方案

前言:上篇C#進階系列——WebApi介面傳參不再困惑:傳參詳解介紹了WebApi引數的傳遞,這篇來看看WebApi裡面異常的處理。關於異常處理,作為程式設計師的我們肯定不陌生,記得在介紹 AOP 的時候,我們講過通過AOP可以統一截獲異常。那麼在我們的WebApi裡面一般是怎麼處理異常的呢,今天這一篇,博主

C#系列——DDD領域驅動設計初探(六):領域服務

前言:之前一直在搭建專案架構的程式碼,有點偏離我們的主題(DDD)了,這篇我們繼續來聊聊DDD裡面另一個比較重要的知識點:領域服務。關於領域服務的使用,書中也介紹得比較晦澀,在此就根據博主自己的理解談談這個知識點的使用。 DDD領域驅動設計初探系列文章: 一、領域服務的引入 在《領域驅動設計:軟體核

C#系列——MEF實現設計上的“鬆耦合”(二)

前言:前篇 C#進階系列——MEF實現設計上的“鬆耦合”(一) 介紹了下MEF的基礎用法,讓我們對MEF有了一個抽象的認識。當然MEF的用法可能不限於此,比如MEF的目錄服務、目錄篩選、重組部件等高階應用在這裡就不做過多講解,因為博主覺得這些用法只有在某些特定的環境下面才會用到,著實不太普遍,感覺沒有鑽下去的

C#系列——DDD領域驅動設計初探(四):WCF搭建

前言:前面三篇分享了下DDD裡面的兩個主要特性:聚合和倉儲。領域層的搭建基本完成,當然還涉及到領域事件和領域服務的部分,後面再專案搭建的過程中慢慢引入,博主的思路是先將整個架構走通,然後一步一步來新增相關元素,使架構慢慢變得豐滿。這篇打算分享下應用層的搭建。根據DDD的設計原則,應用層不包含任何領域邏輯,它主