使用表达式目录树实现SqlDataReader到实体的映射
SqlDataReader映射实体,是ORM的基础功能,常见的实现方式有反射、表达式目录树和emit,这里要说的就是用表达式目录树生成实体的方法。
先分析下思路:
假设有个数据实体类,Student
public class Student
{
public int Id { get; set; } public string Name { get; set; } public int ClazzId { get; set; }
}
获取到SqlDataReader,手撸代码的话,我们可能是这样做的
public Student ConvertToStudent(SqlDataReader sdr)
{
var entity = new Student
{
Id = sdr.GetInt32(),
Name = sdr.GetString(),
ClazzId = sdr.GetInt32()
}; return entity;
}
无疑,这种效率是最高的,原因是:一、直接通过索引获取Reader内的数据;二、直接为属性赋值,而无需通过PropertyInfo.SetValue,既避开了反射,又无需对数据类型进行转换,减少了装箱拆箱的操作。
但是,我们需要的是一个通用方法,上面的写法只能针对具体类型,所以我们真正需要的是这样一个类似的泛型版本
public T ConvertToEntity<T>(SqlDataReader reader) where T : new()
{
var t = new T(); t.a = reader.GetInt32();
t.b = reader.GetString(); return t;
}
问题来了,我们并不知道实体的具体定义,所以上面的代码根本无法实现。所以现在需要解决两个问题:
1、要获取到T的所有属性,这里假设实体是个POCO模型
2、为T专门构建一个方法,实现类似ConvertToStudent()那样的功能
第一个问题很简单,反射,要动态获取实体的属性,这一步是不可避免的,通用做法就是先做一次反射,然后把实体的PropertyInfo缓存起来,这样只要反射一次,这点性能损耗还是可以承受的
第二个问题比较麻烦,动态生成方法,在我的知识体系内是没有办法的,但恰巧知道一个动态生成委托的方法,那就是表达式目录树,我们知道,委托delegate 就是对方法的封装,动态生成委托,正好可以解决眼下的问题
现在将ConvertToStudent改造成委托,这段代码就是我们构造表达式目录树的模板
private Func<SqlDataReader, Student> ConvertToStudentFunc = (reader) =>
{
var entity = new Student();
entity.Id = reader.GetInt32();
entity.Name = reader.GetString();
entity.ClazzId = reader.GetInt32();
return entity;
};
因为没有找到系统的表达式目录树的资料,所以对语法不甚了解,下面的代码是结合网上的资料一点一点摸索出来的,具体不解释了,怕说错了误人子弟,直接看代码吧:
private static Func<SqlDataReader, T> Converter<T>(IDataReader reader)
{
var sdrParameter = Expression.Parameter(typeof(SqlDataReader), "sdr"); var memberBindings = new List<MemberBinding>(); var properties = typeof(T).GetProperties().Where(p => p.CanWrite); for (var i = ; i < reader.FieldCount; i++)
{
var fieldName = reader.GetName(i);
var property =
properties.SingleOrDefault(p => p.Name == fieldName && p.PropertyType == reader.GetFieldType(i)); if (property == null) continue; var methodName = "GetValue";
if (property.PropertyType == typeof(string))
{
methodName = "GetString";
}
else if (property.PropertyType == typeof(int))
{
methodName = "GetInt32";
}
else if (property.PropertyType == typeof(DateTime))
{
methodName = "GetDateTime";
}
else if (property.PropertyType == typeof(decimal))
{
methodName = "GetDecimal";
}
else if (property.PropertyType == typeof(Guid))
{
methodName = "GetGuid";
}
else if (property.PropertyType == typeof(bool))
{
methodName = "GetBoolean";
}
else
{
continue;
} var methodCall = Expression.Call(sdrParameter,
typeof(SqlDataReader).GetMethod(methodName) ?? throw new InvalidOperationException(),
Expression.Constant(i)); memberBindings.Add(Expression.Bind(property, methodCall));
} var initExpression = Expression.MemberInit(Expression.New(typeof(T)), memberBindings);
return Expression.Lambda<Func<SqlDataReader, T>>(initExpression, sdrParameter).Compile();
}
代码并不严谨,很多东西没做,只是个框框,理解原理用的,下面是使用方法:
public static List<T> Fetch<T>(int count)
{
var list = new List<T>();
Func<SqlDataReader, T> func = null;
var topStr = count <= ? "" : $"TOP {count}";
using (var conn = new SqlConnection(ConfigurationManager.AppSettings["DbConnection"]))
{
using (var command = new SqlCommand($"SELECT {topStr} * FROM {typeof(T).Name}", conn))
{
conn.Open();
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
if (func == null)
{
func = ExpressionShow.ConvertEntity<T>(reader);
list.Add(func(reader));
}
else
{
list.Add(func(reader));
}
}
reader.Close();
}
}
} return list;
}
到这里,基本功能就完成了。
使用表达式目录树实现SqlDataReader到实体的映射的更多相关文章
- 【手撸一个ORM】第四步、Expression(表达式目录树)扩展
到这里,Orm的基架已经搭起来了,接下来就是激动人心的部分,表达式目录树转Sql语句,SqlDataReader转数据实体等等,但是在这之前,我们需要扩展下表达式目录树的方法,以方便后面的相关操作. ...
- .Net工具类--表达式目录树解析DataReader和DataTable
一.概述 在项目中经常会使用SQL去操作数据库,在读取数据的时候返回结果一般是DataReader和DataSet,其中DataaSet里面可以包含多个DataTable. 读取到数据之后,一般情况下 ...
- C#表达式目录树(Expression)
1.什么是表达式目录树 :简单的说是一种语法树,或者说是一种数据结构(Expression) 2.用Lambda声明表达式目录树: Expression<Func<; //表达试目录树的方 ...
- 表达式目录树(Expression)
一:什么是表达式树 Expression我们称为是表达式树,是一种数据结构体,用于存储需要计算,运算的一种结构,这种结构可以只是存储,而不进行运算.通常表达式目录树是配合Lambda一起来使用的,la ...
- 第十九节: 结合【表达式目录树】来封装EF的BaseDal层的方法
一. 简介 该章节,可以说是一个简单轻松的章节,只要你对Expression表达式树.EF的基本使用.泛型有所了解,那么本章节实质上就是一个非常简单的封装章节,便于我们快捷开发. PS:在该章节对于E ...
- 第十五节:Expression表达式目录树(与委托的区别、自行拼接、总结几类实例间的拷贝)
一. 基本介绍 回忆: 最早接触到表达式目录树(Expression)可能要追溯到几年前使用EF早期的时候,发现where方法里的参数是Expression<Func<T,bool> ...
- 【手撸一个ORM】第五步、Expression(表达式目录树)转换为Where子句
说明 在SQL中,查询.修改比较常用到WHERE子句,在这里根据使用场景不同,定义了两个类,一个用于查询,一个用于修改(插入)操作.原因是: 查询操作支持一级导航属性查询,如student.Schoo ...
- Expression表达式目录树
一.初识Expression 1.在上一篇我们讲到了委托(忘记了可以在看看,点赞在看养成习惯),今天要讲的Expression也和委托有一点点关系吧(没有直接关系,只是想要大家看看我其他的文章),Ex ...
- 05.表达式目录树Expression
参考文章 https://www.cnblogs.com/xyh9039/p/12748983.html 1. 基本了解 1.1 Lambda表达式 演变过程 using System; namesp ...
随机推荐
- CentOS7 默认防火墙firewalld
firewalld基础 firewalld是CentOS7源生支持的防火墙,firewalld最大的好处有两个:支持动态更新,不用重启服务:第二个就是加入了防火墙的“zone”概念. firewall ...
- 阿里巴巴fastjson源码阅读(待完成)
git地址:https://github.com/alibaba/fastjson.git
- ctypes模块与pywin32模块
ctypes模块: 主要用于调用c动态链接库. 1.聊聊Python ctypes模块 2.ctypes模块管理 相关网址: pywin32模块: 用于访问win32API函数(win32api模块) ...
- linux 网络编程 inet_pton & inet_ntop函数
#include <arpa/inet.h> int inet_pton(int family,const char * strptr,void * addrptr); 返回:--成功, ...
- zero to one (4)
复盘--天下武功唯快不破 There is no martial art is indefectible, while the fastest speed is the only way for lo ...
- 「NOIP2017」「LuoguP3952」 时间复杂度(模拟,栈
题目描述 小明正在学习一种新的编程语言 A++,刚学会循环语句的他激动地写了好多程序并 给出了他自己算出的时间复杂度,可他的编程老师实在不想一个一个检查小明的程序, 于是你的机会来啦!下面请你编写程序 ...
- mac下nginx的安装
新手初学,按照网上的教程,一步一步来进行安装.把自己的安装流程贴出来. 1 安装nginx需要三方的lib库pcre.因此先下载pcre. 在这里,需要注意的是安装的pcre的版本要与nginx对应. ...
- 云-资讯-Micron-Insight:云的形成方式 — 以及它的发展方向
ylbtech-云-资讯-Micron-Insight:云的形成方式 — 以及它的发展方向 1.返回顶部 1. 云的形成方式 — 以及它的发展方向 当你坐下来开始一天工作的时候,你可能不会考虑到你所做 ...
- <正则吃饺子> :关于微信支付的简单总结说明(一)
关于支付,一直想参与开发,现在根据项目中已有及参见的微信开发文档,将自己对于微信开发的流程进行简单的总结,以备后用和帮助后来者. 一.相关官方文档 微信支付官方文档:https://pay.weixi ...
- [51nod1035]最长的循环节
题意:输出<=n的数中倒数循环节长度最长的那个数 解题关键:http://w3.math.sinica.edu.tw/math_media/d253/25311.pdf https://wenk ...