使用表达式目录树实现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 ...
随机推荐
- Facebook的实时流处理技术——Scuba是Facebook的一个非常快速、分布式的内存数据库,用于实时分析和查询
Scuba,Facebook的一个非常快速.分布式的内存数据库,用于实时分析和查询.是Facebook的回归分析代码.错误报告监控.广告收入监控和性能调试的背后主力. Facebook的实时流处理技术 ...
- Java_HTTP_01_HttpClient
一. 二.参考文档 1. HttpClient官方文档 HttpClient官方文档中文翻译 1.HttpClient 4 实现文件下载 2.httpclient 上传文件.下载文件 3.httpcl ...
- springAOP原理以及概念
需求:1.拦截所有业务方法2.判断用户是否有权限,有权限就让他执行业务方法,没有权限就不允许执行.(是否有权限是根据user是否为null作为判断依据) 思考: 我们该如何实现? 思路1: 我们在每个 ...
- 《java编程思想》读后笔记:二,吸血鬼数字
书本p75中一道读后练习思考题,题目如下: 吸血鬼数字是指位数为偶数的数字,可以有一对数字相乘得到,而这对数字各包含成绩的一半位数的数字,其中从最初的数字中选取的数字可以任意排序.一两个0结尾的数字是 ...
- PHP中读写文件
在PHP中读写文件,可以用到一下内置函数: 1.fopen(创建文件和打开文件) 语法: 复制代码代码如下:fopen(filename,mode) filename,规定要打开的文件.mode,打开 ...
- ACM学习历程——POJ3295 Tautology(搜索,二叉树)
Description WFF 'N PROOF is a logic game played with dice. Each die has six faces representing some ...
- C# 架构模式
单例模式 (Singleton) 单例讲的是当一个类被初次调用时,会产生一个类的实例, 而这个类的实例会贯穿程序的整个生命周期.单例提供了一个全局.唯一的实例. 步骤:1.让类自己创建一个实例:2.提 ...
- zoom在清除浮动中的利用
zoom 是个困惑了好久的元素,今天对它有了个初步的认识 zoom , ie 的专属属性,在其他浏览器中不起作用,它的原本功能是设置或检测对象的缩放比例(只在ie下起作用) 比如 <div ...
- github怎么创建一个项目,怎么添加一个ssh-key的客户
1.第一步:打开https://github.com/,登陆成功.单击猫图标,进入页面,单击[start a project] 第二步:输入项目名称,选择public公有,不收费的.单击确认成功. 第 ...
- 原生app与WebApp的区别
Native App开发Native App开发即我们所称的传统APP开发模式(原生APP开发模式),该开发针对IOS.Android等不同的手机操作系统要采用不同的语言和框架进行开发,该模式通常是由 ...