前一篇擴展了兩個經常使用驗證方法,本文將封裝兩個Lambda表達式操做,用來爲下一篇的查詢擴展服務。html
Lambda表達式是一種簡潔的匿名函數語法,能夠用它將方法做爲委託參數傳遞。在Linq中,大量使用Lambda表達式進行查詢,不過這種Lambda表達式被Expression包裝成表達式樹。表達式樹是解釋器的一個實現,它的做用是將一種語法轉換爲另外一種語法,好比將Lambda表達式解析爲Sql語句。express
使用Sql列名進行查詢的主要問題是,列名是一個字符串,沒有智能提示,若是輸入錯誤,也沒有編譯時檢查。使用Lambda表達式查詢能夠解決這些問題,這是使用強類型的主要好處,另外當列名與屬性名不一致時,只需修改映射配置,業務代碼不動,從而加強了系統的擴展性。框架
Lambda表達式的強類型在帶來諸多好處的同時,也產生了一些問題,好比t => t.Name==」a」這個表達式,若是想把值」a」拿出來進行操做,怎麼作到?值」a」對於查詢條件來說,是一個動態傳入的參數,若是對錶達式樹徹底不瞭解,這也不是一件輕鬆的事。另外還有動態查詢的問題,這時候開始懷念弱類型的字符串了,微軟提供了一個動態查詢的輔助類來解決這個問題,待用到的時候我再介紹。 函數
本文介紹的一個方法是GetValue,用來將Lambda謂詞表達式中的值取出來,另外一個方法是GetCriteriaCount,用來判斷Lambda謂詞表達式中條件的個數。這兩個方法的具體應用將在下一篇介紹。單元測試
在Util項目中添加一個Lambda類,代碼以下。測試
using System.Linq; using System.Linq.Expressions; namespace Util { /// <summary> /// Lambda表達式操做 /// </summary> public class Lambda { #region GetValue(獲取值) /// <summary> /// 獲取值,範例:t => t.Name == "A",返回 A /// </summary> /// <param name="expression">表達式,範例:t => t.Name == "A"</param> public static object GetValue( LambdaExpression expression ) { if ( expression == null ) return null; BinaryExpression binaryExpression = GetBinaryExpression( expression ); if ( binaryExpression != null ) return GetBinaryValue( binaryExpression ); var callExpression = expression.Body as MethodCallExpression; if ( callExpression != null ) return GetMethodValue( callExpression ); return null; } /// <summary> /// 獲取二元表達式 /// </summary> private static BinaryExpression GetBinaryExpression( LambdaExpression expression ) { var binaryExpression = expression.Body as BinaryExpression; if ( binaryExpression != null ) return binaryExpression; var unaryExpression = expression.Body as UnaryExpression; if ( unaryExpression == null ) return null; return unaryExpression.Operand as BinaryExpression; } /// <summary> /// 獲取二元表達式的值 /// </summary> private static object GetBinaryValue( BinaryExpression binaryExpression ) { var unaryExpression = binaryExpression.Right as UnaryExpression; if ( unaryExpression != null ) return GetConstantValue( unaryExpression.Operand ); return GetConstantValue( binaryExpression.Right ); } /// <summary> /// 獲取常量值 /// </summary> private static object GetConstantValue( Expression expression ) { var constantExpression = expression as ConstantExpression; if ( constantExpression == null ) return null; return constantExpression.Value; } /// <summary> /// 獲取方法調用表達式的值 /// </summary> private static object GetMethodValue( MethodCallExpression callExpression ) { var argumentExpression = callExpression.Arguments.FirstOrDefault(); return GetConstantValue( argumentExpression ); } #endregion #region GetCriteriaCount(獲取謂詞條件的個數) /// <summary> /// 獲取謂詞條件的個數 /// </summary> /// <param name="expression">謂詞表達式,範例:t => t.Name == "A"</param> public static int GetCriteriaCount( LambdaExpression expression ) { if ( expression == null ) return 0; var result = expression.ToString().Replace( "AndAlso", "|" ).Replace( "OrElse", "|" ); return result.Split( '|' ).Count(); } #endregion } }
爲了進行測試,須要建立一個測試樣例類Test,代碼以下。this
namespace Util.Tests.Samples { /// <summary> /// 測試 /// </summary> public class Test { public string Name { get; set; } public int Age { get; set; } public int? NullableInt { get; set; } public decimal? NullableDecimal { get; set; } public TestA A { get; set; } public class TestA { public int Integer { get; set; } public string Address { get; set; } public TestB B { get; set; } public class TestB { public string Name { get; set; } } } } }
單元測試代碼以下。spa
using System; using System.Linq.Expressions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Util.Tests.Samples; namespace Util.Tests { /// <summary> /// Lambda表達式操做測試 /// </summary> [TestClass] public class LambdaTest { #region GetValue(獲取成員值) /// <summary> /// 獲取成員值,委託返回類型爲Object /// </summary> [TestMethod] public void TestGetValue_Object() { Expression<Func<Test, object>> expression = test => test.Name == "A"; Assert.AreEqual( "A", Lambda.GetValue( expression ) ); } /// <summary> /// 獲取成員值,委託返回類型爲bool /// </summary> [TestMethod] public void TestGetValue_Boolean() { //空值返回null Assert.AreEqual( null, Lambda.GetValue( null ) ); //一級返回值 Expression<Func<Test, bool>> expression = test => test.Name == "A"; Assert.AreEqual( "A", Lambda.GetValue( expression ) ); //二級返回值 Expression<Func<Test, bool>> expression2 = test => test.A.Integer == 1; Assert.AreEqual( 1, Lambda.GetValue( expression2 ) ); //三級返回值 Expression<Func<Test, bool>> expression3 = test => test.A.B.Name == "B"; Assert.AreEqual( "B", Lambda.GetValue( expression3 ) ); } /// <summary> /// 獲取可空類型的值 /// </summary> [TestMethod] public void TestGetValue_Nullable() { //可空整型 Expression<Func<Test, bool>> expression = test => test.NullableInt == 1; Assert.AreEqual( 1, Lambda.GetValue( expression ) ); //可空decimal expression = test => test.NullableDecimal == 1.5M; Assert.AreEqual( 1.5M, Lambda.GetValue( expression ) ); } /// <summary> /// 獲取成員值,運算符爲方法 /// </summary> [TestMethod] public void TestGetValue_Method() { //1級返回值 Expression<Func<Test, bool>> expression = t => t.Name.Contains( "A" ); Assert.AreEqual( "A", Lambda.GetValue( expression ) ); //二級返回值 expression = t => t.A.Address.Contains( "B" ); Assert.AreEqual( "B", Lambda.GetValue( expression ) ); //三級返回值 expression = t => t.A.B.Name.StartsWith( "C" ); Assert.AreEqual( "C", Lambda.GetValue( expression ) ); } #endregion #region GetCriteriaCount(獲取謂詞條件的個數) /// <summary> /// 獲取謂詞條件的個數 /// </summary> [TestMethod] public void TestGetCriteriaCount() { //0個條件 Assert.AreEqual( 0, Lambda.GetCriteriaCount( null ) ); //1個條件 Expression<Func<Test, bool>> expression = test => test.Name == "A"; Assert.AreEqual( 1, Lambda.GetCriteriaCount( expression ) ); //2個條件,與鏈接符 expression = test => test.Name == "A" && test.Name == "B"; Assert.AreEqual( 2, Lambda.GetCriteriaCount( expression ) ); //2個條件,或鏈接符 expression = test => test.Name == "A" || test.Name == "B"; Assert.AreEqual( 2, Lambda.GetCriteriaCount( expression ) ); //3個條件 expression = test => test.Name == "A" && test.Name == "B" || test.Name == "C"; Assert.AreEqual( 3, Lambda.GetCriteriaCount( expression ) ); //3個條件,包括導航屬性 expression = test => test.A.Address == "A" && test.Name == "B" || test.Name == "C"; Assert.AreEqual( 3, Lambda.GetCriteriaCount( expression ) ); } /// <summary> /// 獲取謂詞條件的個數,運算符爲方法 /// </summary> [TestMethod] public void TestGetCriteriaCount_Method() { //1個條件 Expression<Func<Test, bool>> expression = t => t.Name.Contains( "A" ); Assert.AreEqual( 1, Lambda.GetCriteriaCount( expression ) ); //2個條件,與鏈接 expression = t => t.Name.Contains( "A" ) && t.Name == "A"; Assert.AreEqual( 2, Lambda.GetCriteriaCount( expression ) ); //2個條件,或鏈接,包含導航屬性 expression = t => t.Name.Contains( "A" ) || t.A.Address == "A"; Assert.AreEqual( 2, Lambda.GetCriteriaCount( expression ) ); } #endregion } }
須要注意的是,GetValue方法不只要能獲取t=>t.Name==」a」這樣的二元表達式,還要能獲取方法調用表達式中的值,好比t=>t.Name.Contains(「a」)。code
下面再增長一個擴展方法,在Util項目中添加名爲Extensions.Expression的文件,代碼以下。htm
using System; using System.Linq.Expressions; namespace Util { /// <summary> /// 表達式擴展 /// </summary> public static partial class Extensions { #region Value(獲取lambda表達式的值) /// <summary> /// 獲取lambda表達式的值 /// </summary> /// <typeparam name="T">對象類型</typeparam> public static object Value<T>( this Expression<Func<T, bool>> expression ) { return Lambda.GetValue( expression ); } #endregion } }
Lambda表達式不只在查詢上大展身手,並且在表現層,好比Mvc上也有大量的應用。本文只介紹下一篇基礎查詢擴展須要用到的兩個方法,其它方法我會在須要用到的時候補充進來。
.Net應用程序框架交流QQ羣: 386092459,歡迎有興趣的朋友加入討論。
謝謝你們的持續關注,個人博客地址:http://www.cnblogs.com/xiadao521/
若是須要下載代碼,請參考Util應用程序框架公共操做類(六):驗證擴展