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>()

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

  1. C#学习笔记四: C#3.0自动属性匿名属性及扩展方法

    前言 这一章算是看这本书最大的收获了, Lambda表达式让人用着屡试不爽, C#3.0可谓颠覆了我们的代码编写风格. 因为Lambda所需篇幅挺大, 所以先总结C#3.0智能编译器给我们带来的诸多好 ...

  2. EXCEL,如果单元格会自动显示满格--被人设置了自动填充,改回来的方法

    1 问题 如果你或者别人,不小心,让单元格变成了,自动显示满的效果,如下图,改怎么修改回来呢? 这种现实特点就是,如果单元格够长,是当前文本内容的长度整数倍,就显示为整数倍数,余数不管. 2 改回来的 ...

  3. Mybatis-plus的自动填充功能

    1.概述 在我们开发过程中,经常需要做一些插入操作,有些麻烦,特别是对于一些每次插入或修改都需要改变的的一些字段,例如创建时间和修改时间两个字段,和数据没什么关系但是每次修改都需要被动修改这两个字段, ...

  4. php yanzhengm,ThinkPHP 在使用M方法(不创建模型类)时实现自动验证与自动填充

    ThinkPHP M方法实现自动验证与自动填充 通常,我们会将自动验证与自动填充规则写入模型类里,然后再用 D 方法实例化模型类来实现对表单的自动验证与自动填充功能.由于 M 方法只是实例化基础模型类 ...

  5. ajax给表格填值,填报表用ajax实现关联单元格自动填充

    在做填填报表用ajax实现关联单元格自动填充">报表.如果是简单的加减乘除运算,可以在单元格的自动计算里面实现:如果关联单元格显示的内容是从数据库中取出来的,就不能写加减乘除表达式来实 ...

  6. 360 html禁止自动填充,360浏览器的表单自动填充设置在哪?360浏览器自动填充表单的问题怎么解决?...

    360浏览器的表单自动填充设置在哪?360浏览器自动填充表单的问题怎么解决? 很多用户都遇到了360浏览器自动填充表单的问题,那么怎么解决呢?今天带来360浏览器自动填充表单的问题解决方法分享. 开发 ...

  7. input密码输入框自动填充和提示

    input密码输入框在前端会自动填充和提示的解决方法之一(谷歌浏览器) 1.原因 (个人猜测未证实)浏览器记住密码后,页面有密码输入框标签,浏览器会根据id和name属性从密码域中找到匹配的密码进行填 ...

  8. input修改自动填充背景色(单一色以及透明)

    前言 最近想做一个比较好看的登录界面,就将输入框的背景色什么的都改了一遍,测试时基本效果都达到预期,唯独自动填充账号密码时,自己设置的输入框样式不起作用了,样式又变回默认的自动填充色了,这让我很苦恼- ...

  9. Android获取短信验证码并自动填充的两种方式

    有些项目为了方便客户操作,减去客户输入短信验证码的时间,会要求安卓app能够获取收到的短信验证码并自动填充到输入框.所以,我整理了安卓获取短信验证码并自动填充输入框的两种方法,而且正式在项目中使用并无 ...

最新文章

  1. Tip #6 用ASP.NET AJAX判断当前浏览器类型
  2. Spring基于状态机squirrel-foundation简单使用
  3. 第五节 CImage和CBmp(二)
  4. python实现单例模式的三种方式及相关知识解释
  5. rootfs 制作ubuntu_Ubuntu12笔记: 基于busybox的Linux小系统制作
  6. kali系统apt设置代理
  7. 【JEECG技术博文】Local storage easyui extensions
  8. php 抓取 wordpress 文字内容,如何抓取WordPress文章
  9. 拿下微软、Google、Adobe,印度为何盛产科技圈 CEO?
  10. 第8章 数据库实例与操作模式
  11. 读取.properties配置文件(转载)
  12. 开启服务器防火墙 局域网内连接sqlserver数据库问题
  13. csv文件中文乱码转换
  14. httpd离线安装(含依赖包下载地址)
  15. ElasticSearch封装查询、多条件查询、模糊查询工具类
  16. 2020,中国互联网半坡起步
  17. 网上查询类网站!太恐怖了,什么都能查!!!!!(请注意最后)特实用!收藏吧!
  18. 编写函数求特殊a串和
  19. EBS功能_固定资产总结
  20. 概率统计之——方差分析

热门文章

  1. 线程queue、事件event及协程
  2. for each....in、for in、for of
  3. Python3 matplotlib的绘图函数subplot()简介
  4. bzoj1128 Lam-lights
  5. java中static详解
  6. 创业感悟:技术兄弟为什么一直没有起来(1)
  7. Centos7下安装netstat
  8. ConcurrentDictionary的ToDictionary
  9. 安卓中bundle的使用
  10. Python3中的hasattr()