• Util应用程序框架公共操作类(七):Lambda表达式公共操作类


      前一篇扩展了两个常用验证方法,本文将封装两个Lambda表达式操作,用来为下一篇的查询扩展服务。

      Lambda表达式是一种简洁的匿名函数语法,可以用它将方法作为委托参数传递。在Linq中,大量使用Lambda表达式进行查询,不过这种Lambda表达式被Expression包装成表达式树。表达式树是解释器的一个实现,它的作用是将一种语法转换为另一种语法,比如将Lambda表达式解析为Sql语句。

      使用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,代码如下。

    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; }
                }
            }
        }
    }

        单元测试代码如下。

    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”)。

      下面再增加一个扩展方法,在Util项目中添加名为Extensions.Expression的文件,代码如下。

    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应用程序框架公共操作类(六):验证扩展

  • 相关阅读:
    DataList嵌套DataList
    Enterprise Library: Data Access Application Block类设计分析篇
    一些web开发中常用的、做成cs文件的js代码 搜刮来的
    DataList小结
    kill 某个线程
    在JBOSS下发布Web程序
    调整命令行的列数和行数 mode con: cols=100 lines=10000
    了解JavaScript类
    http
    Notepad++文本比较插件:Compare
  • 原文地址:https://www.cnblogs.com/xiadao521/p/4182266.html
Copyright © 2020-2023  润新知