整理:C#中Expression表达式的妙用
一、目的:通过示例了解C#中Expression表达式的作用,通过表达式和反射可以写出很优雅的代码和架构,也可以完成一些看似不可能完成的任务
二、示例:
1、通过表达式获取成员属性
定义模型:
-
[Description("唯一标识")]
-
class PersonModel
-
{
-
[Description("唯一标识")]
-
public string ID { get; set; }
-
-
[Description("名称")]
-
public string Name { get; set; }
-
-
[Description("值")]
-
public double Value { get; set; }
-
-
[Description("年齡")]
-
public double Age { get; set; }
-
-
[Description("收入")]
-
public double InCome { get; set; }
-
-
[Description("支出")]
-
public double Pay { get; set; }
-
}
定义获取模型属性和描述信息的表达式方法
-
/// <summary>
-
/// 通过Linq表达式获取成员属性
-
/// </summary>
-
/// <typeparam name="T"></typeparam>
-
/// <param name="instance"></param>
-
/// <param name="expression"></param>
-
/// <returns></returns>
-
public Tuple<string, string> GetPropertyValue<T>(T instance, Expression<Func<T, string>> expression)
-
{
-
MemberExpression memberExpression = expression.Body as MemberExpression;
-
-
string propertyName = memberExpression.Member.Name;
-
-
string attributeName = (memberExpression.Member.GetCustomAttributes(false)[0] as DescriptionAttribute).Description;
-
-
var property = typeof(T).GetProperties().Where(l => l.Name == propertyName).First();
-
-
return new Tuple<string, string>(attributeName, property.GetValue(instance).ToString());
-
-
}
应用示例:
-
[TestMethod]
-
public void TestMethod1()
-
{
-
// Message:根据表达式获取对应属性的值
-
PersonModel model = new PersonModel();
-
model.ID = "1";
-
model.Name = "王杰";
-
model.Value = 90;
-
model.InCome = 100;
-
model.Pay = 200;
-
model.Age = 33;
-
-
var result = this.GetPropertyValue(model, l => l.Name);
-
-
Debug.WriteLine($"显示名称:{result.Item1}-值:{result.Item2}");
-
-
}
输出结果:
Description:名称 - Name:王杰
也许有人会问,真是多此一举,直接model.Name就可以了,在此示例只演示表达式的应用方法,应用表达式代替Lambda表达式的好处在于Expression是一个结构体,包含了表达式中需要的信息,如l.Name通过Expression表达式方式传入可以很方便的获取到Name这个string,对应用反射创建数据非常有用,后面介绍一个示例,同个此示例可以根据数据库表快速生成想要的报表非常方便;
2、通过表达式快速生成报表
定义应用表达式动态创建的求和报表生成方法
-
/// <summary>
-
/// 获取汇总求和数据
-
/// </summary>
-
/// <typeparam name="T"></typeparam>
-
/// <param name="collection"></param>
-
/// <param name="groupby"></param>
-
/// <param name="expressions"></param>
-
/// <returns></returns>
-
public DataTable GetSum<T>(IQueryable<T> collection, Expression<Func<T, String>> groupby, params Expression<Func<T, double>>[] expressions)
-
{
-
DataTable table = new DataTable();
-
-
// Message:利用表达式设置列名称
-
MemberExpression memberExpression = groupby.Body as MemberExpression;
-
-
var displayName = (memberExpression.Member.GetCustomAttributes(false)[0] as DescriptionAttribute).Description;
-
-
table.Columns.Add(new DataColumn(displayName));
-
-
foreach (var expression in expressions)
-
{
-
memberExpression = expression.Body as MemberExpression;
-
-
displayName = (memberExpression.Member.GetCustomAttributes(false)[0] as DescriptionAttribute).Description;
-
-
table.Columns.Add(new DataColumn(displayName));
-
-
}
-
-
// Message:通过表达式设置数据体
-
var groups = collection.GroupBy(groupby);
-
-
foreach (var group in groups)
-
{
-
// Message:设置分组列头
-
DataRow dataRow = table.NewRow();
-
dataRow[0] = group.Key;
-
-
// Message:设置分组汇总数据
-
for (int i = 0; i < expressions.Length; i++)
-
{
-
var expression = expressions[i];
-
-
Func<T, double> fun = expression.Compile();
-
-
dataRow[i + 1] = group.Sum(fun);
-
}
-
-
table.Rows.Add(dataRow);
-
}
-
-
return table;
-
-
}
应用示例
-
[TestMethod]
-
public void TestMethod2()
-
{
-
// Message:根据表达式获取对应属性的值
-
List<PersonModel> models = new List<PersonModel>();
-
-
Random r = new Random();
-
-
string[] names = { "张学友", "王杰", "刘德华", "张曼玉", "李连杰", "孙悟空" };
-
-
// Message:构造测试数据
-
for (int i = 0; i < 80; i++)
-
{
-
PersonModel model = new PersonModel();
-
model.ID = i.ToString();
-
model.Name = names[r.Next(6)];
-
model.Value = r.Next(20, 100);
-
model.InCome = r.Next(20, 100);
-
model.Pay = r.Next(20, 100);
-
model.Age = r.Next(20, 100);
-
models.Add(model);
-
}
-
-
// Message:生成自定义报表
-
DataTable dt = this.GetSum(models.AsQueryable(), l => l.Name, l => l.Value, l => l.Age);
-
-
WriteTable(dt);
-
}
-
-
public void WriteTable(DataTable dt)
-
{
-
string colums = string.Empty;
-
;
-
foreach (DataColumn item in dt.Columns)
-
{
-
colums += item.ColumnName.PadRight(5,' ') + " ";
-
}
-
-
Debug.WriteLine(colums);
-
-
foreach (DataRow item in dt.Rows)
-
{
-
string rows = string.Empty;
-
-
for (int i = 0; i < dt.Columns.Count; i++)
-
{
-
rows += item[i].ToString().PadRight(5, ' ') + " ";
-
}
-
-
Debug.WriteLine(rows);
-
}
-
}
输出结果:
名称 值 年齡
孙悟空 1018 820
李连杰 737 972
刘德华 655 614
张曼玉 1160 1000
王杰 791 663
张学友 517 435
可以看到
DataTable dt = this.GetSum(models.AsQueryable(), l => l.Name, l => l.Value, l => l.Age);
我们只需要传入
l => l.Name汇总分组表达式
l => l.Value按Value属性求和
l => l.Age按Age属性求和就会生成报表
同理输入
DataTable dt = this.GetSum(models.AsQueryable(), l => l.Name, l => l.Value, l => l.Age, l => l.InCome, l => l.Pay);
可以汇总到如下报表
名称 值 年齡 收入 支出
张曼玉 965 1090 1025 870
孙悟空 613 534 575 579
刘德华 638 921 856 823
李连杰 906 861 996 794
张学友 811 731 848 881
王杰 501 651 572 734
同理可以按其他字段分组
DataTable dt = this.GetSum(models.AsQueryable(), l => l.ID, l => l.Value, l => l.Age, l => l.InCome, l => l.Pay);
可以汇众到如下报表
唯一标识 值 年齡 收入 支出
0 34 38 68 61
1 45 95 45 62
2 81 68 92 82
3 31 61 94 66
4 90 96 58 90
5 40 46 71 41
如果不应用Expression表达式直接用Lambda表达式同样可以生成报表数据部分,但如果想通过一条语句完成抬头道数据的生成则离不开Expression的帮助了
l => l.Value Lambda表达式会自动转换成Expression<Func<T, double>>
Expression<Func<T, double>>方法通过Func<T, double> fun = expression.Compile();可以转换成Lambda表达式
同时也会发现,[Description("唯一标识")]特性也可以定义一个泛型方法去输入
修改如下方法,增加传入转换抬头的特性参数
-
/// <summary>
-
/// 获取汇总求和数据
-
/// </summary>
-
/// <typeparam name="T"></typeparam>
-
/// <param name="collection"></param>
-
/// <param name="groupby"></param>
-
/// <param name="expressions"></param>
-
/// <returns></returns>
-
public DataTable GetSum<TModel,TAttr>(IQueryable<TModel> collection,Func<TAttr,string> toHeader, Expression<Func<TModel, String>> groupby, params Expression<Func<TModel, double>>[] expressions)
-
{
-
DataTable table = new DataTable();
-
-
// Message:利用表达式设置列名称
-
MemberExpression memberExpression = groupby.Body as MemberExpression;
-
-
var displayName = toHeader((TAttr)memberExpression.Member.GetCustomAttributes(typeof(TAttr),false).First());
-
-
table.Columns.Add(new DataColumn(displayName));
-
-
foreach (var expression in expressions)
-
{
-
memberExpression = expression.Body as MemberExpression;
-
-
displayName = toHeader((TAttr)memberExpression.Member.GetCustomAttributes(typeof(TAttr), false).First());
-
-
table.Columns.Add(new DataColumn(displayName));
-
-
}
-
-
// Message:通过表达式设置数据体
-
var groups = collection.GroupBy(groupby);
-
-
foreach (var group in groups)
-
{
-
// Message:设置分组列头
-
DataRow dataRow = table.NewRow();
-
dataRow[0] = group.Key;
-
-
// Message:设置分组汇总数据
-
for (int i = 0; i < expressions.Length; i++)
-
{
-
var expression = expressions[i];
-
-
Func<TModel, double> fun = expression.Compile();
-
-
dataRow[i + 1] = group.Sum(fun);
-
}
-
-
table.Rows.Add(dataRow);
-
}
-
-
return table;
-
-
}
实体修改如下
-
[Description("唯一标识")]
-
class PersonModel
-
{
-
[Description("唯一标识")]
-
[DesignerCategory("分组一")]
-
public string ID { get; set; }
-
-
[Description("名称")]
-
[DesignerCategory("分组二")]
-
public string Name { get; set; }
-
-
[Description("值")]
-
[DesignerCategory("分组三")]
-
public double Value { get; set; }
-
-
[Description("年齡")]
-
[DesignerCategory("分组四")]
-
public double Age { get; set; }
-
-
[Description("收入")]
-
[DesignerCategory("分组五")]
-
public double InCome { get; set; }
-
-
[Description("支出")]
-
[DesignerCategory("分组六")]
-
public double Pay { get; set; }
-
}
测试代码:
-
[TestMethod]
-
public void TestMethod3()
-
{
-
// Message:根据表达式获取对应属性的值
-
List<PersonModel> models = new List<PersonModel>();
-
-
Random r = new Random();
-
-
string[] names = { "张学友", "王杰", "刘德华", "张曼玉", "李连杰", "孙悟空" };
-
-
// Message:构造测试数据
-
for (int i = 0; i < 80; i++)
-
{
-
PersonModel model = new PersonModel();
-
model.ID = i.ToString();
-
model.Name = names[r.Next(6)];
-
model.Value = r.Next(20, 100);
-
model.InCome = r.Next(20, 100);
-
model.Pay = r.Next(20, 100);
-
model.Age = r.Next(20, 100);
-
models.Add(model);
-
}
-
-
Func<DesignerCategoryAttribute, string> toHeader = l => l.Category;
-
-
// Message:生成自定义报表
-
DataTable dt = this.GetSum(models.AsQueryable(), toHeader, l => l.Name, l => l.Value);
-
-
WriteTable(dt);
-
}
输入出结果如下:
分组二 分组三
张学友 723
张曼玉 1083
孙悟空 481
王杰 822
李连杰 664
刘德华 790
以上简单介绍Expression表达式在自动生成报表中的应用,其中还有许多可扩展部分后续会扩展,如Sum、Max、Min等其他聚合函数的封装,下面演示针对具体聚合函数的整合函数
3、聚合函数表达式封装
针对2中GetSum方法,同样可以应用表达式对参数进行处理,可以针对传入的聚合函数的不同分别出报表
封装方法如下:
-
/// <summary>
-
/// 获取汇总求和数据
-
/// </summary>
-
/// <typeparam name="T"></typeparam>
-
/// <param name="collection"></param>
-
/// <param name="groupby"></param>
-
/// <param name="expressions"></param>
-
/// <returns></returns>
-
DataTable GetReport<T>(IQueryable<T> collection, Expression<Func<T, String>> groupby, params Expression<Func<IQueryable<T>, double>>[] expressions)
-
{
-
-
DataTable table = new DataTable();
-
-
// Message:利用表达式设置列名称
-
-
MemberExpression memberExpression = groupby.Body as MemberExpression;
-
-
var displayName = (memberExpression.Member.GetCustomAttributes(false)[0] as DescriptionAttribute).Description;
-
-
table.Columns.Add(new DataColumn(displayName));
-
-
foreach (var expression in expressions)
-
{
-
MethodCallExpression dynamicExpression = expression.Body as MethodCallExpression;
-
-
string groupName = dynamicExpression.Method.Name;
-
-
UnaryExpression unaryexpression = dynamicExpression.Arguments[1] as UnaryExpression;
-
-
LambdaExpression LambdaExpression = unaryexpression.Operand as LambdaExpression;
-
-
-
memberExpression = LambdaExpression.Body as MemberExpression;
-
-
displayName = (memberExpression.Member.GetCustomAttributes(false)[0] as DescriptionAttribute).Description;
-
-
table.Columns.Add(new DataColumn(displayName+$"({groupName})"));
-
-
}
-
-
// Message:通过表达式设置数据体
-
var groups = collection.GroupBy(groupby);
-
-
foreach (var group in groups)
-
{
-
// Message:设置分组列头
-
DataRow dataRow = table.NewRow();
-
dataRow[0] = group.Key;
-
-
// Message:设置分组汇总数据
-
for (int i = 0; i < expressions.Length; i++)
-
{
-
var expression = expressions[i];
-
-
Func<IQueryable<T>, double> fun = expression.Compile();
-
-
dataRow[i + 1] = fun(group.AsQueryable());
-
}
-
-
table.Rows.Add(dataRow);
-
}
-
-
return table;
-
-
}
运行测试代码如下:
-
[TestMethod]
-
public void TestMethod6()
-
{
-
// Message:根据表达式获取对应属性的值
-
List<PersonModel> models = new List<PersonModel>();
-
-
Random r = new Random();
-
-
string[] names = { "张学友", "王杰", "刘德华", "张曼玉", "李连杰", "孙悟空" };
-
-
// Message:构造测试数据
-
for (int i = 0; i < 80; i++)
-
{
-
PersonModel model = new PersonModel();
-
model.ID = i.ToString();
-
model.Name = names[r.Next(6)];
-
model.Value = r.Next(20, 100);
-
model.InCome = r.Next(20, 100);
-
model.Pay = r.Next(20, 100);
-
model.Age = r.Next(20, 100);
-
models.Add(model);
-
}
-
-
-
// Message:生成自定义报表
-
DataTable dt = this.GetReport(models.AsQueryable(), l => l.Name, l => l.Max(k=>k.InCome), l => l.Min(k => k.InCome), l => l.Sum(k => k.InCome));
-
-
WriteTable(dt);
-
}
运行结果如下:
名称 收入(Max) 收入(Min) 收入(Sum)
王杰 85 22 619
张曼玉 98 24 917
李连杰 99 35 917
孙悟空 96 20 671
刘德华 95 20 957
张学友 98 22 814
由以上结果可以看到
DataTable dt = this.GetReport(models.AsQueryable(), l => l.Name, l => l.Max(k=>k.InCome), l => l.Min(k => k.InCome), l => l.Sum(k => k.InCome));
我们传递了不同的汇总表达式,则会生成对应表达式的报表,包括抬头和数据表,有了上面的方法,我们可以更灵活、更简单的去配置出我们想要的报表,生成报表可能只要一行代码
Expression表达式的在构建时的其他优势在后面也会不断发掘
(未完待续)
整理:C#中Expression表达式的妙用的更多相关文章
- FreeSql之Expression表达式拼接参数扩展
在FreeSql源码中Expression表达式拼接默认最多支持到5个泛型参数,当我们使用表关联比较多的时候,就需要进行扩展. 新建一个类,将命名空间改为System.Linq.Expressions ...
- Spring AOP中pointcut expression表达式解析 及匹配多个条件
Spring中事务控制相关配置: <bean id="txManager" class="org.springframework.jdbc.datasource.D ...
- Expression表达式树
表达式树表示树状数据结构的代码,树状结构中的每个节点都是一个表达式,例如一个方法调用或类似 x < y 的二元运算 1.利用 Lambda 表达式创建表达式树 Expression<Fun ...
- (转) Lambda表达式中的表达式lambda和语句lambda区别
Lambda表达式可分为表达式lambda和语句lambda 表达式lambda:表达式位于 => 运算符右侧的lambda表达式称为表达式lambda (input parameters) = ...
- 委托、匿名委托、Lambda 表达式、Expression表达式树之刨根问底
本篇不是对标题所述之概念的入门文章,重点在阐述它们的异同点和应用场景.各位看官,这里就不啰嗦了,直接上代码. 首先定义一个泛型委托类型,如下: public delegate T Function&l ...
- 关于Expression表达式树的拼接
最近在做项目中遇到一个问题,需求是这样的: 我要对已经存在的用户进行检索,可以根据用户的id 或者用户名其中的一部分字符来检索出来,这样就出现了三种情况 只有id,只有用户名中一部字符,或者全部都有. ...
- Java8中Lambda表达式的10个例子
Java8中Lambda表达式的10个例子 例1 用Lambda表达式实现Runnable接口 //Before Java 8: new Thread(new Runnable() { @Overri ...
- Lambda表达式中的表达式lambda和语句lambda区别
Lambda表达式可分为表达式lambda和语句lambda 表达式lambda:表达式位于 => 运算符右侧的lambda表达式称为表达式lambda (input parameters) = ...
- .net 系列:Expression表达式树、lambda、匿名委托 的使用
首先定义一个泛型委托类型,如下: public delegate T Function<T>(T a, T b); 实现泛型委托的主体代码,并调用: public static strin ...
随机推荐
- Nginx 核心配置-location的登录账户认证实战篇
Nginx 核心配置-location的登录账户认证实战篇 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.使用ab命令模拟网站攻击 1>.安装httpd-tools工具 ...
- maven中,dependency 中的 classifier属性
classifier元素用来帮助定义构件输出的一些附属构件.附属构件与主构件对应,比如主构件是 kimi-app-2.0.0.jar 该项目可能还会通过使用一些插件生成 如 kimi-app-2.0. ...
- @TableName(mybatis-plus中的注解)
@TableName 描述:表名注解 属性 类型 必须指定 默认值 描述 value String 否 "" 表名 schema String 否 "" sch ...
- python爬取全站壁纸代码
#测试网址:https://www.ivsky.com/bizhi/ #需要安装的库:requests,bs4 #本人是个强迫症患者,为了美观添加数个print(),其并没有实际意义,若是不爽删去即可 ...
- destoon开发笔记-调取资讯标题图
今天刚申请博客园的博客,申请速度挺快的.之前我的文章都是发在自己搭建的博客网站,但是是香港服务器,不想续费了,所以就关闭了.之前的数据也没有了,挺可惜了.不过既然加入博客园的大家庭,我就在这每天记录工 ...
- Android: Error inflating class android.support.v4.view.ViewPager 问题的解决方法
ViewPager是个很好很强大的控件,很多应用用它来实现很酷的效果,但是很多情况下在运行时会遇到Error inflating class android.support.v4.view.ViewP ...
- 树莓派项目(1-3 )目标识别 NNPACK支持版Darknet,可用于树莓派等嵌入设备
https://github.com/digitalbrain79/darknet-nnpack https://github.com/AlexeyAB/darknet#how-to-train-to ...
- ABP JS调用接口 获取返回的数据
var _userService = abp.services.app.user; console.log(abp.services.app.user); _userService.getUserBy ...
- 洛谷p3353在你窗外闪耀的星星题解
题目 首先被题目甜到了 本来搜标签搜的线段树,结果发现这题目很吸引我我果断点开 觉得前缀和就能A啊 于是乎 要注意 窗户旁边是可以看到的 所以前缀和的时候是不用再-1的 //前缀和 //注意坑点 // ...
- .NET三层架构开发初步
转自:https://www.cnblogs.com/weilengdeyu/archive/2013/01/04/2844847.html 今天咱们来谈下三层架构.说到三层架构,搞过点程序的可能都知 ...