虽然Dapper通过提供的DbString本身支持对于String的指定Parameterized,但这方法明显不够,当Insert时,我们更希望是把一个Poco直接传递过去,而不是来new一个匿名函数,对于string类型的属性,转化成DbString,然后一个一个的属性再写一遍,这多苦逼

通过代码,可以看到有这么一段方法

public static Action<IDbCommand, object> CreateParamInfoGenerator(Identity identity, bool checkForDuplicates, bool removeUnused)

这段代码就是用来构建Param参数的,内部通过Emit来实现,在里面可以找到遍历属性的代码,其内部有一些判断,这些就是可以直接增加代码来控制生成何种Param的地方,具体是两个地方

if (dbType != DbType.Time)
{
il.Emit(OpCodes.Dup);// stack is now [parameters] [[parameters]] [parameter] [parameter]
EmitInt32(il, (int)dbType);// stack is now [parameters] [[parameters]] [parameter] [parameter] [db-type]
il.EmitCall(OpCodes.Callvirt, typeof(IDataParameter).GetProperty("DbType").GetSetMethod(), null);// stack is now [parameters] [[parameters]] [parameter]
}
if (prop.PropertyType == typeof(string))
{
    il.Emit(OpCodes.Dup); // [string] [string]
    il.EmitCall(OpCodes.Callvirt, typeof(string).GetProperty("Length").GetGetMethod(), null); // [string] [length]
    EmitInt32(il, 4000); // [string] [length] [4000]
    il.Emit(OpCodes.Cgt); // [string] [0 or 1]
    Label isLong = il.DefineLabel(), lenDone = il.DefineLabel();
    il.Emit(OpCodes.Brtrue_S, isLong);
    EmitInt32(il, 4000); // [string] [4000]
    il.Emit(OpCodes.Br_S, lenDone);
    il.MarkLabel(isLong);
    EmitInt32(il, -1); // [string] [-1]
    il.MarkLabel(lenDone);
    il.Emit(OpCodes.Stloc_1); // [string]
}

我们只要修改第一段代码部分的

EmitInt32(il, (int)dbType);

通过修改(int)dbType来控制AnsiString、AnsiStringFixedLength、String、StringFixedLength

通过修改第二段代码部分的两个4000来控制字符串Param长度(其实修改第二个4000就能达到目标,但为啥还要第一个,整段Emit代码又是什么意思。。。完全没看懂!!)

具体怎么改呢

1、可以通过Attribute,这个方法比较简单,但坏处就是相当于破坏了Dapper不需要修改原代码的事实,如果Orm每次通过工具生成一次,就要修改一次Poco,当然你也可以修改Orm的生成工具,为属性加上相应的Length限制,好处就是可以直接应用Dapper自己提供的SqlMapperExtensions,而且相对一致,都是通过Attribute进行控制

2、通过Mapping,这个就是要额外增加控制的类,下面是一个简单的类

public class DapperStringParameterized
{
private Dictionary<string, KeyValuePair<DbType, int>> _dic = new Dictionary<string, KeyValuePair<DbType, int>>();
/// <summary>
/// 添加字符串参数化映射
/// </summary>
/// <param name="name">属性名</param>
/// <param name="type">必须为AnsiString、AnsiStringFixedLength、String、StringFixedLength</param>
/// <param name="len">必须为1~8000</param>
public virtual void Add(string name, DbType type = DbType.AnsiString, int len = 50)
{
if (len <= 0 || len > 8000)
{//长度范围1~8000,此处暂时对应sql,如果其它关系型数据库长度范围与此不一致,可继承修改
throw new ArgumentException("The param len's value must between 1 and 8000.");
}
if (type != DbType.AnsiString && type != DbType.AnsiStringFixedLength && type != DbType.String && type != DbType.StringFixedLength)
{
return;
}
if (!string.IsNullOrEmpty(name))
{
if (this._dic.ContainsKey(name))
{
throw new ArgumentException(string.Format("The param name '{0}' has aready existed!", name));
}
else
{
this._dic.Add(name, new KeyValuePair<DbType, int>(type, len));
}
}
}
public void Remove(string name)
{
if (!string.IsNullOrWhiteSpace(name))
{
if (this._dic.ContainsKey(name))
{
this._dic.Remove(name);
}
}
}
public KeyValuePair<DbType, int>? GetParameterizedData(string name)
{
if (!string.IsNullOrWhiteSpace(name) && this._dic.ContainsKey(name))
{
return this._dic[name];
}
return null;
}
} public class DapperStringParameterizedManager
{
private static readonly DapperStringParameterizedManager manager = new DapperStringParameterizedManager();
private static Dictionary<Type, DapperStringParameterized> dic = new Dictionary<Type, DapperStringParameterized>();
private static object locObj = new object();
private DapperStringParameterizedManager() { } public static DapperStringParameterizedManager Instance
{
get { return manager; }
}
/// <summary>
/// 添加映射关系
/// </summary>
/// <returns></returns>
public void AddMapping<T>(DapperStringParameterized mapping)
where T : class
{
if (mapping != null)
{
lock (locObj)
{
DapperStringParameterized tmpmapping = this.GetMapping(typeof(T));
if (tmpmapping == null)
{
dic.Add(typeof(T), mapping);
}
else
{
throw new ArgumentException(string.Format("The POCO Mapping {0} has aready existed!", typeof(T)));
}
}
}
} public DapperStringParameterized GetMapping(Type type)
{
if (type != null && dic.ContainsKey(type))
{
return dic[type];
}
return null;
}
}

使用时就是在CreateParamInfoGenerator方法中,foreach (var prop in props)之前添加代码

DapperStringParameterized dsp = DapperStringParameterizedManager.Instance.GetMapping(identity.type);

在获取DbType的地方增加代码

DbType dbType = LookupDbType(prop.PropertyType, prop.Name);
KeyValuePair<DbType, int>? kvp = null;
if (dbType == DbType.String && dsp != null)//默认所有字符串在Dapper中被param成 DbType.String
{
kvp = dsp.GetParameterizedData(prop.Name);
}

第一段代码部分修改为

if (dbType != DbType.Time) // https://connect.microsoft.com/VisualStudio/feedback/details/381934/sqlparameter-dbtype-dbtype-time-sets-the-parameter-to-sqldbtype-datetime-instead-of-sqldbtype-time
{
//string parameter extensions 对于字符串参数化的扩展
int dbTypeValue = (int)dbType;
if (kvp.HasValue)
{
dbTypeValue = (int)kvp.Value.Key;
} il.Emit(OpCodes.Dup);// stack is now [parameters] [[parameters]] [parameter] [parameter]
EmitInt32(il, dbTypeValue);// stack is now [parameters] [[parameters]] [parameter] [parameter] [db-type] il.EmitCall(OpCodes.Callvirt, typeof(IDataParameter).GetProperty("DbType").GetSetMethod(), null);// stack is now [parameters] [[parameters]] [parameter]
}

因为我们设定了字符串允许的最大长度,所以第二部分判断大小的代码直接注销,然后将下面另一段判断string的代码

if (prop.PropertyType == typeof(string))
{
var endOfSize = il.DefineLabel();
// don't set if 0
il.Emit(OpCodes.Ldloc_1); // [parameters] [[parameters]] [parameter] [size]
il.Emit(OpCodes.Brfalse_S, endOfSize); // [parameters] [[parameters]] [parameter] il.Emit(OpCodes.Dup);// stack is now [parameters] [[parameters]] [parameter] [parameter]
il.Emit(OpCodes.Ldloc_1); // stack is now [parameters] [[parameters]] [parameter] [parameter] [size]
il.EmitCall(OpCodes.Callvirt, typeof(IDbDataParameter).GetProperty("Size").GetSetMethod(), null); // stack is now [parameters] [[parameters]] [parameter] il.MarkLabel(endOfSize);
}

修改为

if (prop.PropertyType == typeof(string) && kvp.HasValue)
{
il.Emit(OpCodes.Dup);
EmitInt32(il, kvp.Value.Value);
il.EmitCall(OpCodes.Callvirt, typeof(IDbDataParameter).GetProperty("Size").GetSetMethod(), null); // stack is now [parameters] [[parameters]] [parameter]
}

这样如果有设定字符串长度,则此部分代码会进行size设定,否则不设定

实际用的地方只要在static构造函数中添加相应的初始化设定就可以了,建议将此部分代码写在相应的Repository部分,如果是三层则写在DAL部分,比如

static _Default()
{
DapperStringParameterized dsp = new DapperStringParameterized(); DapperStringParameterizedManager manager = DapperStringParameterizedManager.Instance;
manager.AddMapping<Customer>(dsp); dsp.Add("UserName", DbType.String, 20);
dsp.Add("Contact", DbType.String, 25);
}

好吧。。这样子做了之后只针对Query<T>起了作用,对于Execute没起作用,因为这个方法没指定类型T,在创建Identity时它直接将Type设为了null,那就添加ExecuteQ<T>方法,因为指定了T,所以将Execute的代码复制一份,然后将new Identity的地方将cnn后面的第一个null改为typeof(T)就可以了

Dapper.net 在Parameterized时对于String的扩展(转)的更多相关文章

  1. redis存json数据时选择string还是hash

    redis存json数据时选择string还是hash 我们在缓存json数据到redis时经常会面临是选择string类型还是选择hash类型去存储.接下来我从占用空间和IO两方面来分析这两种类型的 ...

  2. ES6新增语法和内置对象(let,const, Array/String/Set 扩展方法(解构赋值,箭头函数,剩余参数))

    1.let ES6中新增的用于声明变量的关键字. let 声明的变量只在所处于的块级有效. 注意:使用 let 关键字声明的变量才具有块级作用域,var 关键字是不具备这个特点的. 1. 防止循环变量 ...

  3. 关于C#不同位数相与或,或赋值时,隐藏位数扩展该留意的问题

    __int64 a; char b; a = b; a |= b; 如上情况,当b的最高位为1时,即b=0x80(或更大)时,b在扩展成64过程中会将最高位向高位扩展变成0xfffffffffffff ...

  4. ExtJS学习-----------Ext.String,ExtJS对javascript中的String的扩展

    关于ExtJS对javascript中的String的扩展,能够參考其帮助文档,文档下载地址:http://download.csdn.net/detail/z1137730824/7748893 以 ...

  5. 第200天:js---常用string原型扩展

    一.常用string原型扩展 1.在字符串末尾追加字符串 /** 在字符串末尾追加字符串 **/ String.prototype.append = function (str) { return t ...

  6. Dapper官方库 在guid和string互转的问题

    之前在和老何谈论他的开源项目Util中使用MySql的过程中发现了官方dapper在转换guid到string时候的一个错误 Error parsing column 0 (ID=6c2adb93-d ...

  7. Dapper多表查询时子表字段为空

    最近在学习使用Dapper时百度了一篇详细使用教程,在做到多表查询的时候,出现如下情况. 使用的SQL如下, SELECT * FROM [Student] AS A INNER JOIN [Juni ...

  8. sqoop导入时删除string类型字段的特殊字符

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/sheismylife/article/details/29384357 假设你指定了\n为sqoop ...

  9. dapper 写查询sql 时,多条件参数操作方法

    var args = new DynamicParameters(new {}); if (obj.orderId != null) { sb.Append(" AND OrderId = ...

随机推荐

  1. poj 3150 Cellular Automaton

    首先来看一下Sample里的第一组数据.1 2 2 1 2经过一次变换之后就成了5 5 5 5 4它的原理就是a0 a1 a2 a3 a4->(a4+a0+a1) (a0+a1+a2) (a1+ ...

  2. C Primer Plus 第3章 数据和C 编程练习

    1. /* 整数上溢 */ #include <stdio.h> int main(void) { ; unsigned ; /* 无符号整数j像一个汽车里程指示表(形容的太好了,可参考& ...

  3. 深入浅出Java并发包—原子类操作

    我们知道,JDK1.5以后引入了并发包(java.util.concurrent)用于解决多CPU时代的并发问题,而并发包中的类大部分是基于Queue的并发类,Queue在大多数情况下使用了原子类(A ...

  4. lintcode 容易题:Partition Array by Odd and Even 奇偶分割数组

    题目: 奇偶分割数组 分割一个整数数组,使得奇数在前偶数在后. 样例 给定 [1, 2, 3, 4],返回 [1, 3, 2, 4]. 挑战 在原数组中完成,不使用额外空间. 解题: 一次快速排序就可 ...

  5. [cocoapods] 如何卸载工程里的cocoapods

    1. 打开自己的工程所在文件夹 删除这4个文件 2. 打开工程,把红线的东西都删除掉 3. 只留下原来的4个,其余都删除掉

  6. ubuntu创建用户

    Linux 系统是一个多用户多任务的分时操作系统,任何一个要使用系统资源的用户,都必须首先向系统管理员申请一个账号,然后以这个账号的身份进入系统.用户的账号 一方面可以帮助系统管理员对使用系统的用户进 ...

  7. Xcode学习

    http://www.cnblogs.com/ygm900/p/3488881.html

  8. 删除 GPT 保护分区

    问题: 将内置和/或外置硬盘连接到 Windows XP 32 位操作系统时,将无法访问硬盘,“磁盘管理”将会报告该硬盘包含 GPT 保护分区.在此状态下,将无法对硬盘进行重新分区和格式化. 原因: ...

  9. [HDOJ2818]Building Block(带权并查集,路径压缩)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2818 题意:有N个块,每次有两个操作: M x y表示把x所在的那一堆全部移到y所在的那一堆的下方. ...

  10. Struts2注解配置之@Namespace(四)

    今天开始学习@Namespace注解. 还是先看一段代码: package com.example.actions; import org.apache.struts2.convention.anno ...