前一篇扩展了两个经常使用验证方法,本文将封装两个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应用程序框架公共操做类(六):验证扩展