【译】将IDataRecord自动填充到实体的扩展方法

Mapper:

Mapper的核心功能是创建一个委托函数并映射到一个目标类的实例。此委托是使用表达式树创建的一个lambda表达式。
在这个函数中有一个双重循环,从 DataRecord 获取字段并和从实体类中获取的属性名称比较从而填充实体实例。
所以第一个要求就是在使用这个 Mapper 时,DataReader的字段名必须匹配将要填充类的属性名且要填充的属性是可写的。
对于每个映射属性检查源和目标类型,不管他们是否为空,不管他们是否需要转换。
我们需要的所有信息中存在 SchemaTable,但是 IDataReader 不处理 null 值。
/// <summary>
/// 从提供的 DataRecord 对象创建新委托实例。
/// </summary>
/// <param name="RecordInstance">表示一个 DataRecord 实例</param>
/// <returns>从提供的 DataRecord 对象创建新委托实例。</returns>
/// <remarks></remarks>
private static Func<Record, Target> GetInstanceCreator(Record RecordInstance)
{List<MemberBinding> Bindings = new List<MemberBinding>();Type TargetType = typeof(Target);Type SourceType = typeof(Record);ParameterExpression SourceInstance = Expression.Parameter(SourceType, "SourceInstance");MethodInfo GetSourcePropertyMethodExpression = SourceType.GetProperty("Item", new Type[] { typeof(int) }).GetGetMethod();DataTable SchemaTable = ((IDataReader)RecordInstance).GetSchemaTable();//通过在目标属性和字段在记录中的循环检查哪些是匹配的for (int i = 0; i <= RecordInstance.FieldCount - 1; i++){foreach (PropertyInfo TargetProperty in TargetType.GetProperties(BindingFlags.Public | BindingFlags.Instance)) {//如果属性名和字段名称是一样的if (TargetProperty.Name.ToLower() == RecordInstance.GetName(i).ToLower() && TargetProperty.CanWrite) {//获取 RecordField 的类型Type RecordFieldType = RecordInstance.GetFieldType(i);//RecordField 可空类型检查if ((bool)(SchemaTable.Rows[i]["AllowDBNull"]) == true && RecordFieldType.IsValueType) {RecordFieldType = typeof(Nullable<>).MakeGenericType(RecordFieldType);}//为 RecordField 创建一个表达式Expression RecordFieldExpression = Expression.Call(SourceInstance, GetSourcePropertyMethodExpression, Expression.Constant(i, typeof(int)));//获取一个表示 SourceValue 的表达式Expression SourceValueExpression = GetSourceValueExpression(RecordFieldType, RecordFieldExpression);Type TargetPropertyType = TargetProperty.PropertyType;//从 RecordField 到 TargetProperty 类型的值转换Expression ConvertedRecordFieldExpression = GetConvertedRecordFieldExpression(RecordFieldType, SourceValueExpression, TargetPropertyType);MethodInfo TargetPropertySetter = TargetProperty.GetSetMethod();//为属性创建绑定var BindExpression = Expression.Bind(TargetPropertySetter, ConvertedRecordFieldExpression);//将绑定添加到绑定列表
                Bindings.Add(BindExpression);}}}//创建 Target 的新实例并绑定到 DataRecordMemberInitExpression Body = Expression.MemberInit(Expression.New(TargetType), Bindings);return Expression.Lambda<Func<Record, Target>>(Body, SourceInstance).Compile();
}

现在我们需要从 IDataReader 创建一个 sourceproperty。

/// <summary>
/// 获取表示 RecordField 真实值的表达式。
/// </summary>
/// <param name="RecordFieldType">表示 RecordField 的类型。</param>
/// <param name="RecordFieldExpression">表示 RecordField 的表达式。</param>
/// <returns>表示 SourceValue 的表达式。</returns>
private static Expression GetSourceValueExpression(Type RecordFieldType, Expression RecordFieldExpression)
{//首先从 RecordField 取消装箱值,以便我们可以使用它UnaryExpression UnboxedRecordFieldExpression = Expression.Convert(RecordFieldExpression, RecordFieldType);//获取一个检查 SourceField 为 null 值的表达式UnaryExpression NullCheckExpression = Expression.IsFalse(Expression.TypeIs(RecordFieldExpression, typeof(DBNull)));ParameterExpression Value = Expression.Variable(RecordFieldType, "Value");//获取一个设置 TargetProperty 值的表达式Expression SourceValueExpression = Expression.Block(new ParameterExpression[] { Value }, Expression.IfThenElse(NullCheckExpression, Expression.Assign(Value, UnboxedRecordFieldExpression),Expression.Assign(Value, Expression.Constant(GetDefaultValue(RecordFieldType), RecordFieldType))), Expression.Convert(Value, RecordFieldType));return SourceValueExpression;
}

现在把源转换到目标属性。

如果它们相同,只需要在装箱之前将源对象分配给目标属性。如果他们不同我们还需要将源对象强制转换为目标类型。

还有一个特殊情况,需要处理这里。没有操作符为原始类型转换为字符串。所以如果我们试试这个函数将抛出异常。这是通过调用 ToString 方法处理源。

/// <summary>
/// Gets an expression representing the recordField converted to the TargetPropertyType
/// </summary>
/// <param name="RecordFieldType">The Type of the RecordField</param>
/// <param name="UnboxedRecordFieldExpression">An Expression representing the Unboxed RecordField value</param>
/// <param name="TargetPropertyType">The Type of the TargetProperty</param>
/// <returns></returns>
private static Expression GetConvertedRecordFieldExpression(Type RecordFieldType, Expression UnboxedRecordFieldExpression, Type TargetPropertyType)
{Expression ConvertedRecordFieldExpression = default(Expression);if (object.ReferenceEquals(TargetPropertyType, RecordFieldType)){//Just assign the unboxed expressionConvertedRecordFieldExpression = UnboxedRecordFieldExpression;}else if (object.ReferenceEquals(TargetPropertyType, typeof(string))){//There are no casts from primitive types to String.//And Expression.Convert Method (Expression, Type, MethodInfo) only works with static methods.ConvertedRecordFieldExpression = Expression.Call(UnboxedRecordFieldExpression, RecordFieldType.GetMethod("ToString", Type.EmptyTypes));}else{//Using Expression.Convert works wherever you can make an explicit or implicit cast.//But it casts OR unboxes an object, therefore the double cast. First unbox to the SourceType and then cast to the TargetType//It also doesn't convert a numerical type to a String or date, this will throw an exception.ConvertedRecordFieldExpression = Expression.Convert(UnboxedRecordFieldExpression, TargetPropertyType);}return ConvertedRecordFieldExpression;
}

为了使用其更快,我们将使用缓存

/// <summary>
/// A Singleton construct that returns a precompiled delegate if it exists, otherwise it will create one
/// </summary>
/// <param name="RecordInstance"></param>
/// <returns></returns>
static internal Func<Record, Target> GetCreator(Record RecordInstance)
{if (_Creator == null){lock (SyncRoot){if (_Creator == null){//Get Creator on first access_Creator = GetInstanceCreator(RecordInstance);}}}return _Creator;
}

 使用示例:

公有方法包括两个简单的扩展方法,所以 IDataRecord 所以使用映射器是很容易的。

/// <summary>
/// ExtensionMethod that creates a List<Target> from the supplied IDataReader
/// </summary>
/// <param name="Reader"></param>
/// <returns></returns>
public static List<Target> ToList<Target>(this IDataReader Reader) where Target : class, new()
{List<Target> List = new List<Target>();while (Reader.Read()){List.Add(CreateInstance<Target>(Reader));}return List;
}/// <summary>
/// ExtensionMethod that Creates an instance<Target>) from a DataRecord.
/// </summary>
/// <param name="Record">The DataRecord containing the values to set on new instance</param>
/// <returns>An instance of Target class</returns>
public static Target CreateInstance<Target>( this IDataRecord Record) where Target : class, new()
{return (Mapper<IDataRecord, Target>.GetCreator(Record))(Record);
}
可以像这样使用它:Reader.CreateInstance<MyClassInstance>
Reader.ToList<MyClass>()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/265191.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

U-Boot 图形化配置及其原理

目录U-Boot 图形化配置体验&#xff08;如何使能dns 命令&#xff09;menuconfig 图形化配置原理make menuconfig 过程分析Kconfig 语法简介1、mainmenu2、调用其他目录下的Kconfig 文件3、menu/endmenu 条目3、config 条目4、depends on 和select4、choice/endchoice5、menuco…

类与对象(三)

定义一个描述教师的类Teacher&#xff0c;数据成员包括工号(Num),姓名(Name、性别(Sex、家庭住址( Addr}、联系电话(Tel}, E-mail地址(Email )、职务(Headship )、职称(Post)和工资(Salary对于数据成员&#xff0c;要求用字符数组实现工号、姓名、家庭住址、联系电话、E-mail地…

OpenStack tokens id获取测试

转载于:https://www.cnblogs.com/heidsoft/p/3524711.html

Linux 内核获取、初次编译、源码目录分析

目录Linux 内核获取Linux 内核初次编译Linux 内核源码目录分析1、arch 目录2、block 目录3、crypto 目录4、Documentation 目录5、drivers 目录6、firmware 目录7、fs 目录8、include 目录9、init 目录10、ipc 目录11、kernel 目录12、lib 目录13、mm 目录Linux 内核获取 关于L…

bzoj 3157 bzoj 3516 国王奇遇记 —— 推式子

题目&#xff1a;https://www.lydsy.com/JudgeOnline/problem.php?id3157 https://www.lydsy.com/JudgeOnline/problem.php?id3516 这篇博客写得太好&#xff1a;http://blog.miskcoo.com/2014/06/bzoj-3157 然而目前之会 \( O(m) \) 的做法&#xff1b; 感觉关键是设计 \( S…

PHP里10个鲜为人知但却非常有用的函数

PHP里有非常丰富的内置函数&#xff0c;很多我们都用过&#xff0c;但仍有很多的函数我们大部分人都不熟悉&#xff0c;可它们却十分的有用。这篇文章里&#xff0c;我列举了一些鲜为人知但会让你眼睛一亮的PHP函数。 levenshtein() 你有没有经历过需要知道两个单词有多大的不同…

前端(jQuery UI)(2)-- jQuery UI interactions

之后补充转载于:https://www.cnblogs.com/foreverlin/p/10127982.html

学会阅读硬件的原理图、数据手册大全

参考&#xff1a; 郭天祥&#xff1a;https://www.bilibili.com/video/BV1DW411a7mz?p8 韦东山&#xff1a;https://www.bilibili.com/video/BV1ga4y1Y7PL?p4 https://www.bilibili.com/video/BV17g411F7oR?spm_id_from333.999.0.0 洋桃电子&#xff1a;https://www.bilibil…

解决替换weblogic的commons-fileupload.jar后引发的问题

为什么80%的码农都做不了架构师&#xff1f;>>> 上一篇博文中提到通过替换weblogic自带的commons-fileupload.jar来解决FileUpload类的NoSuchMethodError问题。在完成替换后&#xff0c;该问题得到顺利解决&#xff0c;但是也会引发其他的一些问题&#xff0c;下面…

Python for Data Analysis 学习心得(二) - pandas介绍

一、pandas介绍 本篇程序上篇内容&#xff0c;在numpy下面继续介绍pandas&#xff0c;本书的作者是pandas的作者之一。pandas是非常好用的数据预处理工具&#xff0c;pandas下面有两个数据结构&#xff0c;分别为Series和DataFrame&#xff0c;DataFrame之前我在一些实战案例中…

别人总结的一些git教程大全

工作中&#xff0c;除了必备的基础知识&#xff0c;还要学会与人合作。 如何将你开发的小功能整合到整个项目的大框架中如何让你的实验性代码不影响到大框架中的代码性能如何让你放下手中写到一半的代码去修改突然出现的bug 这些都是会出现的情况&#xff0c;为了应对这些情况…

Euler:欧拉函数&素数筛

一、欧拉函数 欧拉函数是小于x的整数中与x互质的数的个数&#xff0c;一般用φ(x)表示。 通式&#xff1a; 其中p1, p2……pn为x的所有质因数&#xff0c;x是不为0的整数。比如x12&#xff0c;拆成质因数为122*2*3&#xff0c;12以内有1/2的数是2的倍数&#xff0c;那么有1-1/2…

大小端字节序

想起以前在汇编语言和数字逻辑的时候也有接触到一些这个概念&#xff0c;已经有点模糊了&#xff0c;搞不清楚哪个是低位在前哪个是高位在前。后来在Wiki和Google的帮助下也算摸清楚了一些Endianness的概念。 一、字节序的起源 在计算机中&#xff0c;字节序&#xff08;Endian…

面经——嵌入式软件工程师2021面试指南【转】

目录 作者简介面试前准备资料推荐要不要刷笔试题、力扣企业资讯获取简历制作去不去外包面试自我介绍技术面试HR面我的面试经历基础媒体层知识项目谈薪资最后作者简介 2021应届本二,目前年薪20,在某行业头部大厂从事嵌入式多媒体开发(音视频应用方向)。 由于没有耀眼的学历…

docker 部署nginx 使用keepalived 部署高可用

一&#xff0e;体系架构 在Keepalived Nginx高可用负载均衡架构中&#xff0c;keepalived负责实现High-availability (HA) 功能控制前端机VIP&#xff08;虚拟网络地址&#xff09;&#xff0c;当有设备发生故障时&#xff0c;热备服务器可以瞬间将VIP自动切换过来&#xff0c…

【原】自定义UINavigationItem的两种方法以及相应的隐藏方法

第一种&#xff1a; UIImage *searchimage[UIImage imageNamed:"search.png"]; UIBarButtonItem *barbtn[[[UIBarButtonItem alloc] initWithImage:nil style:UIBarButtonItemStyleDone target:self action:selector(searchprogram)] autoRelease]; barbtn.image…

虚拟字符设备驱动开发步骤

目录前言字符设备驱动简介内核驱动操作函数集合(file_operations结构体)字符设备驱动开发步骤.ko驱动模块的加载和卸载(module_init驱动入口、insmod驱动加载)字符设备注册与注销到内核register_chrdev(设备号、设备名) -- 很少用了实现设备的具体操作函数添加LICENSE 和作者信…

Jackson学习笔记

Java下常见的Json类库有Gson、JSON-lib和Jackson等&#xff0c;Jackson相对来说比较高效&#xff0c;在项目中主要使用Jackson进行JSON和Java对象转换&#xff0c;下面给出一些Jackson的JSON操作方法。 一、准备工作 Jackson有1.x系列和2.x系列&#xff0c;2.x系列有3个jar包需…

设计模式20——Mediator设计模式

2019独角兽企业重金招聘Python工程师标准>>> Mediator中介者设计模式是通过一个中介对象封装一系列关于对象交互行为. Mediator中介者设计模式中的角色如下&#xff1a; (1).中介者&#xff08;Mediator&#xff09;&#xff1a;抽象定义了“同事”&#xff08;co…

Linux LED驱动开发实验(直接操作寄存器 -- 实际开发很少这样做)

目录Linux 下LED 灯驱动原理地址映射(ioremap映射、iounmap释放)I/O 内存访问函数硬件原理图分析实验程序编写LED 灯驱动程序编写APP测试程序编写运行测试编译驱动程序和测试APP拷贝led.ko 和ledApp到指定目录加载led.ko 驱动模块到内核创建应用层“/dev/led”设备节点运行测试…