一、前言

Dapper是.NET下一个micro的ORM,它和Entity Framework或Nhibnate不同,属于轻量级的,并且是半自动的。Dapper只有一个代码文件,完全开源,你可以放在项目里的任何位置,来实现数据到对象的ORM操作,体积小速度快。 使用ORM的好处是增、删、改很快,不用自己写sql,因为这都是重复技术含量低的工作,还有就是程序中大量的从数据库中读数据然后创建model,并为model字段赋值。这些ORM都可以轻松给你搞定。ORM给我们开发带来便利时,性能也是一个让我们不得不考虑的问题。一般的ORM性能和直接写原生的sql比都差不少,但是Dapper性能还很错,甚至和DbHelperSQL方式性能高出很多。

二、问题

在使用Dapper做查询的时候,数据库字段名和Model属性名一一对应时,直接只用dapper方法是没有问题的,比如:

public class UserModel
{public int UserId { get; set; }public string Mobile { get; set; }public string UserName { get; set; }
}

但通常数据库的一些规范中,字段名中的单词之间是通过下划线_来拼接的,如

create_time

那么,在C#程序中,使用Dapper做查询时,如何配置数据表字段(列)和实体类属性之间的映射呢?

若属性名和数据库字段不一致(不区分大小写)则查询不出数据,如果使用EF则可以通过Column特性,建立属性和数据表字段之间的映射关系,Dapper则不行,如何让Dapper也支持通过Column特性映射。

在网上看到其他比较好的解决方案,就是通过SqlMapper.ITypeMap自己手动映射

但是还是没有实现自动映射,于是基于别人的手动映射我做了一套自动映射的方案,放在文章下方,我们先看一下手动映射的方案:

以下内容来自博主 Zdelta

Dapper数据库字段和model属性映射_Zdelta的博客-CSDN博客_dapper映射

Dapper虽然有colmun的特性,但是并不能完成字段-属性映射;

我们需要手动拓展一个colmun特性出来,以完成如EF的绑定。

1,添加一个类ColumnAttributeTypeMapper,用于字段-属性映射:

using Dapper;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;namespace 命名空间
{/// <summary>/// Uses the Name value of the <see cref="ColumnAttribute"/> specified to determine/// the association between the name of the column in the query results and the member to/// which it will be extracted. If no column mapping is present all members are mapped as/// usual./// </summary>/// <typeparam name="T">The type of the object that this association between the mapper applies to.</typeparam>public class ColumnAttributeTypeMapper<T> : FallbackTypeMapper{public ColumnAttributeTypeMapper(): base(new SqlMapper.ITypeMap[]{new CustomPropertyTypeMap(typeof(T),(type, columnName) =>type.GetProperties().FirstOrDefault(prop =>prop.GetCustomAttributes(false).OfType<ColumnAttribute>().Any(attr => attr.Name == columnName))),new DefaultTypeMap(typeof(T))}){}}[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]public class ColumnAttribute : Attribute{public string Name { get; set; }}public class FallbackTypeMapper : SqlMapper.ITypeMap{private readonly IEnumerable<SqlMapper.ITypeMap> _mappers;public FallbackTypeMapper(IEnumerable<SqlMapper.ITypeMap> mappers){_mappers = mappers;}public ConstructorInfo FindConstructor(string[] names, Type[] types){foreach (var mapper in _mappers){try{ConstructorInfo result = mapper.FindConstructor(names, types);if (result != null){return result;}}catch (NotImplementedException){}}return null;}public SqlMapper.IMemberMap GetConstructorParameter(ConstructorInfo constructor, string columnName){foreach (var mapper in _mappers){try{var result = mapper.GetConstructorParameter(constructor, columnName);if (result != null){return result;}}catch (NotImplementedException){}}return null;}public SqlMapper.IMemberMap GetMember(string columnName){foreach (var mapper in _mappers){try{var result = mapper.GetMember(columnName);if (result != null){return result;}}catch (NotImplementedException){}}return null;}public ConstructorInfo FindExplicitConstructor(){return _mappers.Select(mapper => mapper.FindExplicitConstructor()).FirstOrDefault(result => result != null);}}
}

2,再添加一个类ColumnMapper,用于添加映射关系:

using Dapper;
using 引入需要的.Models;namespace 项目命名空间
{public class ColumnMapper{public static void SetMapper(){//数据库字段名和c#属性名不一致,手动添加映射关系SqlMapper.SetTypeMap(typeof(Books), new  ColumnAttributeTypeMapper<Books>());//每个需要用到[colmun(Name="")]特性的model,都要在这里添加映射}}
}

3,在starup.cs类的中方法注册:

public void ConfigureServices(IServiceCollection services)
{services.AddMvc();services.AddSession();//调用前面的静态方法,将映射关系注册ColumnMapper.SetMapper();
}

4,最后就可以在model的属性名上添加特性来映射到数据库字段名了:

using 引入新加的类.Helper;public class Books
{[Column(Name = "create_time")]public DateTime CreateTime { get; set; }
}

这样我们就可以在所有的不与数据库对应的model中,方便的添加映射关系了!

虽然怎样就可以实现了Dapper数据库字段和model属性映射了,但是每个需要用到[colmun(Name="")]特性的model,都需要ColumnMapper.SetMapper()方法中去增加映射,开发起来还是极其的不爽的,有没有办法可以支持自动注入映射呢

三、我的方案

在程序启动的时候,通过反射查找出所有的用到[colmun(Name="")]特性的model,自动增加映射

话不多说直接看代码

1,添加一个类ColumnAttributeTypeMapper,用于字段-属性映射:

using Dapper;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Reflection;namespace 命名空间
{public class ColumnAttributeTypeMapper<T> : FallbackTypeMapper{public ColumnAttributeTypeMapper(): base(new SqlMapper.ITypeMap[]{new CustomPropertyTypeMap(typeof(T),(type, columnName) =>type.GetProperties().FirstOrDefault(prop =>prop.GetCustomAttributes(false).OfType<ColumnAttribute>().Any(attr => attr.Name == columnName))),new DefaultTypeMap(typeof(T))}){}}/// <summary>/// 我自定义的映射/// </summary>public class ColumnAttributeTypeMapper : FallbackTypeMapper{public ColumnAttributeTypeMapper(Type type): base(new SqlMapper.ITypeMap[]{new CustomPropertyTypeMap(type,(type, columnName) =>type.GetProperties().FirstOrDefault(prop =>prop.GetCustomAttributes(false).OfType<ColumnAttribute>().Any(attr => attr.Name == columnName))),new DefaultTypeMap(type)}){}public ColumnAttributeTypeMapper(Type type, IEnumerable<PropertyInfo> propertyInfos): base(new SqlMapper.ITypeMap[]{new CustomPropertyTypeMap(type, (type, columnName) =>propertyInfos.FirstOrDefault(prop =>(prop.GetCustomAttribute(typeof(ColumnAttribute),false) as ColumnAttribute)?.Name == columnName)),new DefaultTypeMap(type)}){}}public class FallbackTypeMapper : SqlMapper.ITypeMap{private readonly IEnumerable<SqlMapper.ITypeMap> _mappers;public FallbackTypeMapper(IEnumerable<SqlMapper.ITypeMap> mappers){_mappers = mappers;}public ConstructorInfo FindConstructor(string[] names, Type[] types){foreach (var mapper in _mappers){try{ConstructorInfo result = mapper.FindConstructor(names, types);if (result != null){return result;}}catch (NotImplementedException){}}return null;}public SqlMapper.IMemberMap GetConstructorParameter(ConstructorInfo constructor, string columnName){foreach (var mapper in _mappers){try{var result = mapper.GetConstructorParameter(constructor, columnName);if (result != null){return result;}}catch (NotImplementedException){}}return null;}public SqlMapper.IMemberMap GetMember(string columnName){foreach (var mapper in _mappers){try{var result = mapper.GetMember(columnName);if (result != null){return result;}}catch (NotImplementedException){}}return null;}public ConstructorInfo FindExplicitConstructor(){return _mappers.Select(mapper => mapper.FindExplicitConstructor()).FirstOrDefault(result => result != null);}}
}

2,再添加一个类ColumnMapper,用于通过反射查找出所有的用到[colmun(Name="")]特性的model,自动增加映射

using Dapper;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Reflection;namespace 命名空间
{/// <summary>/// 用于通过反射查找出所有的用到[colmun(Name="")]特性的model,自动增加映射/// </summary>public class ColumnMapper{/// <summary>/// 用于通过反射查找出所有的用到[colmun(Name="")]特性的model,自动增加映射/// </summary>/// <param name="assemblyName">类型的所在程序集</param>/// <param name="namespaceName">类型命名空间前缀</param>public static void RegisterColumnAttributeTypeMapper(string assemblyName, string namespaceName){if (!string.IsNullOrEmpty(assemblyName) && !string.IsNullOrEmpty(namespaceName)){//二选其一//1 //var typeList = FindCustomAttributesTypes(assemblyName, dataMapperNamespace);//typeList.AsParallel().ForAll(type => SqlMapper.SetTypeMap(type, new ColumnAttributeTypeMapper(type)));//2var properties= FindCustomAttributesPropertyInfos(assemblyName, namespaceName);properties.AsParallel().ForAll(item => SqlMapper.SetTypeMap(item.type, new ColumnAttributeTypeMapper(item.type, item.propertyInfos)));}}/// <summary>/// 查找所有类型/// </summary>/// <param name="assemblyName">类型的所在程序集</param>/// <param name="namespaceName">类型命名空间前缀</param>/// <returns></returns>public static IEnumerable<Type> FindCustomAttributesTypes(string assemblyName, string namespaceName){var assembly = Assembly.Load(assemblyName);if (assembly == null){throw new ArgumentNullException("FindTypes assembly");}var types = assembly.GetTypes().Where(type => !string.IsNullOrEmpty(type.Namespace) && (type.Namespace.Equals(namespaceName) || type.Namespace.StartsWith(namespaceName + "."))).Where(item =>{var propertyInfoList = item.GetProperties();return propertyInfoList.Any() && propertyInfoList.Any(p => p.GetCustomAttribute(typeof(ColumnAttribute)) != null);});if (!types.Any()){throw new ArgumentNullException("FindTypes types");}return types;}/// <summary>/// 查找所有属性/// </summary>/// <param name="assemblyName">类型的所在程序集</param>/// <param name="namespaceName">类型命名空间前缀</param>/// <returns></returns>/// <exception cref="ArgumentNullException"></exception>public static IEnumerable<(Type type, IEnumerable<PropertyInfo> propertyInfos)> FindCustomAttributesPropertyInfos(string assemblyName, string namespaceName){ConcurrentBag<(Type type, IEnumerable<PropertyInfo>)> properties = new ConcurrentBag<(Type type, IEnumerable<PropertyInfo>)> { };var assembly = Assembly.Load(assemblyName);if (assembly == null){throw new ArgumentNullException("FindTypes assembly");}assembly.GetTypes().Where(type => !string.IsNullOrEmpty(type.Namespace) && (type.Namespace.Equals(namespaceName) || type.Namespace.StartsWith(namespaceName + "."))).AsParallel().ForAll(type => {var propertyInfoList = type.GetProperties().Where(p => p.GetCustomAttribute(typeof(ColumnAttribute), false) != null);if (propertyInfoList.Any()){properties.Add((type, propertyInfoList));SqlMapper.SetTypeMap(type, new ColumnAttributeTypeMapper(type, propertyInfoList));}});return properties;}}
}

3,在Program.cs类或starup.cs 的中注册:

namespace Qxq.Framework.Repository.PostgreSql.Test
{internal class Program{static async Task Main(string[] args){ColumnMapper.RegisterColumnAttributeTypeMapper("类型的所在程序集", "类型命名空间前缀");}}
}
public void ConfigureServices(IServiceCollection services)
{ColumnMapper.RegisterColumnAttributeTypeMapper("类型的所在程序集", "类型命名空间前缀");
}

4,最后就可以在model的属性名上添加特性来映射到数据库字段名了:

public class UserModel
{[Column("user_id")]public int UserId { get; set; }public string Mobile { get; set; }[Column("create_date")]public DateTime CreateDate { get; set; }
}

没有用到[colmun(Name="")]特性的属性会根据属性名称匹配映射(不区分大小写)

用到[colmun(Name="")]特性的属性会根据colmun中定义的Name去匹配映射

这样我们就可以在所有的不与数据库对应的model的属性中,使用[colmun(Name="")]标记数据库字段明,就可以很方便的添加映射关系了!

Dapper数据库字段(列)与实体属性名不一致,通过Column特性自动注入映射相关推荐

  1. Mybatis 解决数据库字段名和实体类属性名不一致问题

    一.问题描述 1.1 查询 password 字段为 null 现象 数据库字段名: 实体类属性名: @Data @NoArgsConstructor @AllArgsConstructor publ ...

  2. Mybatis处理字段名和属性名不一致的几种方法

    Mybatis处理字段名和属性名不一致的几种方法 1.为查询的字段设置别名,和属性名保持一致2.当字段符合MySQL的要求使用,而属性符合Java的要求使用驼峰-此时可以在mybatis核心配置文件中 ...

  3. 数据库字段类型、实体类字段类型、mapper文件jdbcType三者对应关系

    数据库字段类型.实体类字段类型.mapper文件jdbcType三者对应关系: 数据库字段类型 实体类字段类型 mapper文件jdbcType bigint Long JdbcType.BIGINT ...

  4. SSM_Mybatis_Day01(快速入门、映射文件概述、核心配置文件概述、相应API、代理开发方式、映射文件深入、数据类型的映射、列名和属性名不一致的时候的处理)

    SSM_Mybatis_Day01(快速入门.映射文件概述.核心配置文件概述.相应API.代理开发方式.映射文件深入.数据类型的映射.列名和属性名不一致的时候的处理) 1. Mybatis mybat ...

  5. mybatis学习(18):列名与属性名不一致的情况(使用ResultMap)

    目录结构 com.geyao.mybatis.mapper BlogMapper类 package com.geyao.mybatis.mapper;import com.geyao.mybatis. ...

  6. mybatis学习(17):列名与属性名不一致的情况(使用别名)

    目录结构 com.geyao.mybatis.mapper BlogMapper类 package com.geyao.mybatis.mapper;import com.geyao.mybatis. ...

  7. oracle 数据库字段名与实体类字段名称不匹配的处理方法

    之前公司一直都使用sql server 即使数据库字段名称与实体类名称不相同 可以使用诸如: select id as userId from tb_user 这种写法,可换到了oracle 之后坑爹 ...

  8. 如何配置数据库带有下划线字段对应Java实体类属性(驼峰命名)

    一般开发中,数据库字段设计推荐使用下划线(u_name),Java实体类属性使用驼峰命名(uName),为了能使数据库字段与Java实体类属性一一映射,需要做一下的配置,这里我用的是spring bo ...

  9. 【Mybatis 之应用篇】2_配置解析、属性名问题、日志、分页和注解开发

    文章目录 Mabatis 四.配置解析 1.核心配置文件 2.environments(环境配置)☆ 3.properties(属性)☆ 4.typeAliases(类型别名)☆ 5.settings ...

最新文章

  1. ilm 和dlm差异_电力通信系统--加密芯片.pdf
  2. 2011.12.15 linux基本命令
  3. mysql主从同步 sql_mysql主从同步报错;Slave_SQL_Running: No
  4. 香农定理和奈奎斯特定理区别_这一切都从指数函数开始(4)——采样定理
  5. mysql5.7 glibcxx_3.4.15_Requires: libstdc++.so.6(GLIBCXX_3.4.15)(64bit)
  6. boost::weak_from_raw相关的测试程序
  7. springboot 权限管理 后台框架源码 java 项目 shiro FHAddmin
  8. 044-PHP获得多个类对应的反射信息
  9. 猎鹰spacex_SpaceX:简单,美观的界面是未来
  10. 深入理解 JVM Class文件格式(八)
  11. 教授呼吁:应当让博士生先回学校
  12. 图像太宽无法输出请裁剪图像或降低分辨率然后重试_真·无监督!延世大学提出图像到图像无监督模型,实验结果超SOTA...
  13. HTML5 的定位一些重要知识点
  14. ubuntu14.04.5安装paramiko模块pip install paramiko出现一堆问题的解决过程
  15. python爬虫qq音乐_Python爬虫-QQ音乐无损音乐地址解析
  16. dell 戴尔电脑官网保修期查询或驱动下载安装
  17. oracle 通过同义词创建视图
  18. 计算机无法访问,您可能没有权限使用网络资源.请与这台服务器的管理员联系的解决办
  19. 3 - Error writing file 'C:\Windows\TEMP\MY18F3.tmp' (Errcode: 28)
  20. 拯救rm-rf删库事故

热门文章

  1. c语言打开一个html文件路径,C语言文件处理 -C语言文件的打开和关闭
  2. h5唤醒微信支付PHP,app内嵌微信h5支付,支付服务唤起支付处理
  3. Linux学习笔记(22.1)——基于SPI + Regmap + IIO的ICM20608设备驱动
  4. 英语语法(2)----点破主谓宾系表三大句型
  5. 用python制作微信机器人程序编写_Python制作微信聊天机器人
  6. 部门培训之练习今天全部干翻篇
  7. 线程3 boost::future
  8. 猫游记页游mysql_全球游戏:“端转手”、“出海”是趋势性方向
  9. springboot根据cron获取任务执行上次和下次执行时间
  10. 小觅深度相机kalibr标定