目录:

Dapper源码学习和源码修改(上篇主要讲解入参解析)

Dapper源码学习和源码修改(下篇主要讲解出参解析)

之前ORM比较火热,自己也搞了个WangSql,但是感觉比较low,大家都说Dapper性能好,所以现在学习学习Dapper,下面简单从宏观层面讲讲我学习的Dapper。

再了解一个东西前,先得学会使用,我也不再赘述怎么使用,接转一个文章吧

http://www.cnblogs.com/yankliu-vip/p/4182892.html

好就当学习了吧,该去看看源码了,到底怎么实现和好在哪呢。

先上一张图,已经把SqlMapper.cs按类拆分了,同时我自己在学习过程中也删了加了改了一些类。

当然最重要的类还是SqlMapper.cs这个类,那就开始吧。

本来想把这个SqlMapper.cs类代码全部粘贴的发现太长了,就算了吧,就把一些关键代码粘贴过来。

        private static CacheInfo GetCacheInfo(Identity identity)
{
CacheInfo info;
if (!TryGetQueryCache(identity, out info))
{
info = new CacheInfo();
if (identity.parametersType != null)
{
if (typeof(string).IsAssignableFrom(identity.parametersType))
{
info.ParamReader = delegate(IDbCommand cmd, object obj) { (new StringParameters() as IDynamicParameters).AddParameters(cmd, identity, obj); };
}
else if (typeof(IDictionary).IsAssignableFrom(identity.parametersType))
{
info.ParamReader = delegate(IDbCommand cmd, object obj) { (new DictionaryParameters() as IDynamicParameters).AddParameters(cmd, identity, obj); };
}
else
{
info.ParamReader = CreateParamInfoGenerator(identity);
}
}
SetQueryCache(identity, info);
}
return info;
}

来来来,划重点了 info.ParamReader = CreateParamInfoGenerator(identity); 看到没,这货是干嘛的啊,哪里用的呢?

就这里用的,其实就是那是一个委托,主要用来创建Command的DataParameter的,不信看下面

        private static IDbCommand SetupCommand(IDbConnection cnn, IDbTransaction transaction, string sql, Action<IDbCommand, object> paramReader, object obj, int? commandTimeout, CommandType? commandType)
{
var cmd = cnn.CreateCommand();
var bindByName = GetBindByName(cmd.GetType());
if (bindByName != null) bindByName(cmd, true);
cmd.Transaction = transaction;
cmd.CommandText = FormatSql(sql);
if (commandTimeout.HasValue)
cmd.CommandTimeout = commandTimeout.Value;
if (commandType.HasValue)
cmd.CommandType = commandType.Value;
if (paramReader != null)
{
paramReader(cmd, obj);
}
return cmd;
}

又划重点了, paramReader(cmd, obj); 这里就是执行委托创建Command的DataParameter了。

如果有人问为什么要这个呢,那就是你上面连Dapper基本使用都没看啊,滚回去看看先。

举个例子:

                sql = "insert into Teacher(Id,Name) values(@Id,@Name)";
string tid = Guid.NewGuid().ToString();
teacher = new Teacher()
{
Id = tid,
Name = "wang"
};
intResult = SqlMapper.Execute(conn, sql, teacher);

这里 SqlMapper.Execute(conn, sql, teacher); 参数teacher就是上面 paramReader(cmd, obj);对应的参数obj,这个委托呢就是将自定义实体teacher变成cmd的Parameters。

那么你又要问了,怎么变的?额,这么嘛就是难点了....

回到上面看看,委托的创建 info.ParamReader = CreateParamInfoGenerator(identity);

关键点也是难点啊同学们CreateParamInfoGenerator这个方法是干嘛的啊,就是创建委托的啊,你个白痴。

        public static Action<IDbCommand, object> CreateParamInfoGenerator(Identity identity)
{
Type type = identity.parametersType;
bool filterParams = identity.commandType.GetValueOrDefault(CommandType.Text) == CommandType.Text; var dm = new DynamicMethod(string.Format("ParamInfo{0}", Guid.NewGuid()), null, new[] { typeof(IDbCommand), typeof(object) }, type, true); var il = dm.GetILGenerator(); il.DeclareLocal(type); //
bool haveInt32Arg1 = false;
il.Emit(OpCodes.Ldarg_1); // stack is now [untyped-param]
il.Emit(OpCodes.Unbox_Any, type); // stack is now [typed-param]
il.Emit(OpCodes.Stloc_0);// stack is now empty il.Emit(OpCodes.Ldarg_0); // stack is now [command]
il.EmitCall(OpCodes.Callvirt, typeof(IDbCommand).GetProperty("Parameters").GetGetMethod(), null); // stack is now [parameters] IEnumerable<PropertyInfo> props = type.GetProperties();
if (filterParams)
{
props = FilterParameters(props, identity.sql);
}
foreach (var prop in props)
{
if (filterParams)
{
if (identity.sql.IndexOf(dbProvider.ParameterPrefix + prop.Name, StringComparison.InvariantCultureIgnoreCase) <
&& identity.sql.IndexOf("#" + prop.Name + "#", StringComparison.InvariantCultureIgnoreCase) <
)
{ // can't see the parameter in the text (even in a comment, etc) - burn it with fire
continue;
}
}
//if (prop.PropertyType == typeof(DbString))
//{
// il.Emit(OpCodes.Ldloc_0); // stack is now [parameters] [typed-param]
// il.Emit(OpCodes.Callvirt, prop.GetGetMethod()); // stack is [parameters] [dbstring]
// il.Emit(OpCodes.Ldarg_0); // stack is now [parameters] [dbstring] [command]
// il.Emit(OpCodes.Ldstr, prop.Name); // stack is now [parameters] [dbstring] [command] [name]
// il.EmitCall(OpCodes.Callvirt, typeof(DbString).GetMethod("AddParameter"), null); // stack is now [parameters]
// continue;
//}
DbType dbType = LookupDbType(prop.PropertyType, prop.Name);
if (dbType == DbType.Xml)
{
// this actually represents special handling for list types;
il.Emit(OpCodes.Ldarg_0); // stack is now [parameters] [command]
il.Emit(OpCodes.Ldstr, prop.Name); // stack is now [parameters] [command] [name]
il.Emit(OpCodes.Ldloc_0); // stack is now [parameters] [command] [name] [typed-param]
il.Emit(OpCodes.Callvirt, prop.GetGetMethod()); // stack is [parameters] [command] [name] [typed-value]
if (prop.PropertyType.IsValueType)
{
il.Emit(OpCodes.Box, prop.PropertyType); // stack is [parameters] [command] [name] [boxed-value]
}
il.EmitCall(OpCodes.Call, typeof(SqlMapper).GetMethod("PackListParameters"), null); // stack is [parameters]
continue;
}
il.Emit(OpCodes.Dup); // stack is now [parameters] [parameters] il.Emit(OpCodes.Ldarg_0); // stack is now [parameters] [parameters] [command]
il.EmitCall(OpCodes.Callvirt, typeof(IDbCommand).GetMethod("CreateParameter"), null);// stack is now [parameters] [parameters] [parameter] il.Emit(OpCodes.Dup);// stack is now [parameters] [parameters] [parameter] [parameter]
il.Emit(OpCodes.Ldstr, FormatNameForParameter(prop.Name)); // stack is now [parameters] [parameters] [parameter] [parameter] [name]
il.EmitCall(OpCodes.Callvirt, typeof(IDataParameter).GetProperty("ParameterName").GetSetMethod(), null);// stack is now [parameters] [parameters] [parameter] 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] il.Emit(OpCodes.Dup);// stack is now [parameters] [parameters] [parameter] [parameter]
EmitInt32(il, (int)ParameterDirection.Input);// stack is now [parameters] [parameters] [parameter] [parameter] [dir]
il.EmitCall(OpCodes.Callvirt, typeof(IDataParameter).GetProperty("Direction").GetSetMethod(), null);// stack is now [parameters] [parameters] [parameter] il.Emit(OpCodes.Dup);// stack is now [parameters] [parameters] [parameter] [parameter]
il.Emit(OpCodes.Ldloc_0); // stack is now [parameters] [parameters] [parameter] [parameter] [typed-param]
il.Emit(OpCodes.Callvirt, prop.GetGetMethod()); // stack is [parameters] [parameters] [parameter] [parameter] [typed-value]
bool checkForNull = true;
if (prop.PropertyType.IsValueType)
{
il.Emit(OpCodes.Box, prop.PropertyType); // stack is [parameters] [parameters] [parameter] [parameter] [boxed-value]
if (Nullable.GetUnderlyingType(prop.PropertyType) == null)
{ // struct but not Nullable<T>; boxed value cannot be null
checkForNull = false;
}
}
if (checkForNull)
{
if (dbType == DbType.String && !haveInt32Arg1)
{
il.DeclareLocal(typeof(int));
haveInt32Arg1 = true;
}
// relative stack: [boxed value]
il.Emit(OpCodes.Dup);// relative stack: [boxed value] [boxed value]
Label notNull = il.DefineLabel();
Label? allDone = dbType == DbType.String ? il.DefineLabel() : (Label?)null;
il.Emit(OpCodes.Brtrue_S, notNull);
// relative stack [boxed value = null]
il.Emit(OpCodes.Pop); // relative stack empty
il.Emit(OpCodes.Ldsfld, typeof(DBNull).GetField("Value")); // relative stack [DBNull]
if (dbType == DbType.String)
{
EmitInt32(il, );
il.Emit(OpCodes.Stloc_1);
}
if (allDone != null) il.Emit(OpCodes.Br_S, allDone.Value);
il.MarkLabel(notNull);
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, ); // [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, ); // [string] [4000]
il.Emit(OpCodes.Br_S, lenDone);
il.MarkLabel(isLong);
EmitInt32(il, -); // [string] [-1]
il.MarkLabel(lenDone);
il.Emit(OpCodes.Stloc_1); // [string]
}
if (allDone != null) il.MarkLabel(allDone.Value);
// relative stack [boxed value or DBNull]
}
il.EmitCall(OpCodes.Callvirt, typeof(IDataParameter).GetProperty("Value").GetSetMethod(), null);// stack is now [parameters] [parameters] [parameter] 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);
} il.EmitCall(OpCodes.Callvirt, typeof(IList).GetMethod("Add"), null); // stack is now [parameters]
il.Emit(OpCodes.Pop); // IList.Add returns the new index (int); we don't care
}
// stack is currently [command]
il.Emit(OpCodes.Pop); // stack is now empty
il.Emit(OpCodes.Ret);
return (Action<IDbCommand, object>)dm.CreateDelegate(typeof(Action<IDbCommand, object>));
}

好了,方法给你了,自己看吧,注释写的多详细的啊。恩....

WTF,英文的啊,不要紧有翻译的,反正自己看吧,我来给你讲怕误人子弟啊。

就我删减后的Dapper 来说入参解析部分其实就到此结束,什么鬼,毫无亮点,要你讲有何用。

别急,别急,下面讲讲我修改部分。

使用中,我发现参数的这么写 insert into Student(Id,Name,Tid) values(@Id,@Name,@Tid) 那如果我换了数据库比如MySql又得改@为?,换成Oracle又得把@改成:,这是我不能忍受的其一。

使用中,我发现 sql = "delete from Student where Id=#Id#"; intResult = SqlMapper.Execute(conn, sql, "jkajskajsk"); 报错,为毛啊,连字符串入参传入你都不认识,这是我不能忍受其二。

以上问题,可能是我版本问题,我用的是Dapper NET2.0版本。

不管为什么,先解决这个两个痛点。

问题一:

原因分析,主要是不知道数据库类型造成的。

解决办法,那我就提前告诉你,数据库相关信息,我们采用一种驱动方式来设置数据库相关信息。

我新建了个类DbProvider.cs

    internal class DbProvider
{
public bool UseParameterPrefixInSql { get; set; }
public bool UseParameterPrefixInParameter { get; set; }
public string ParameterPrefix { get; set; }
}

很简单,后期你可以自己扩展,  ParameterPrefix  这个就是数据库参数前缀,比如@ ? :

            //dbProvider
dbProvider = new DbProvider()
{
UseParameterPrefixInSql = true,
UseParameterPrefixInParameter = true,
ParameterPrefix = "@"
};

在SqlMapper创建一个 dbProvider 构造函数里面 对其初始化,具体值最好写在web.config里面,初始化的时候去读配置文件。

有了这个之前的SQL我们可以改改了 insert into Student(Id,Name,Tid) values(#Id#,#Name#,#Tid#)当然原来的写法也是支持的,只不过现在这种写法,保证的SQL参数的统一性,以后切换数据库也容易多了。

可是这样写了,能正常运行吗?答案是NO,所以还需要下面的方法。

        //我写的
internal static string FormatNameForSql(string parameterName)
{
return dbProvider.UseParameterPrefixInSql ? (dbProvider.ParameterPrefix + parameterName) : parameterName;
}
internal static string FormatNameForParameter(string parameterName)
{
return dbProvider.UseParameterPrefixInParameter ? (dbProvider.ParameterPrefix + parameterName) : parameterName;
}
internal static string FormatSql(string sql)
{
Regex regex = new Regex("#([a-zA-Z0-9_]+?)#", RegexOptions.IgnoreCase | RegexOptions.Multiline);
var ms = regex.Matches(sql);
foreach (Match item in ms)
{
sql = sql.Replace(item.Value, FormatNameForSql(item.Groups[].Value));
}
return sql;
}

主要是这个方法 FormatSql 什么时候调呢,在这里

好了,问题一,反正是解决了,下面看看问题二了。

问题二:

原因分析,

来来来,划重点了 info.ParamReader = CreateParamInfoGenerator(identity); 看到没,这货是干嘛的啊,哪里用的呢?

就这里用的,其实就是那是一个委托,主要用来创建Command的DataParameter的

引用的上面的,那个委托啊CreateParamInfoGenerator不支持String、Dictionary这种入参造成的。

解决办法,既然那个委托不支持,我就给不同的类型创建不同的委托就行了啥。

我为继承string类型的创建了一个委托,委托是执行StringParameters实例的AddParameters方法。

我为继承IDictionary类型的创建了一个委托,委托是执行DictionaryParameters实例的AddParameters方法。

通过不同的委托就能实现不同入参实现给Command的Parameters创建赋值了,哈哈哈哈哈....当然你要实现int double ...都一样的方法,加个类继承IDynamicParameters即可。

现在这样子都可以正常使用了

                sql = "delete from Student where Id=#Id#";
intResult = SqlMapper.Execute(conn, sql, "jkajskajsk"); sql = "delete from Student where Id=#Id# and Name=@Name and Name=@Name1";
Hashtable dic = new Hashtable();
dic.Add("Id", "");
dic.Add("Name", "s1234");
dic.Add("Name1", "d12345");
intResult = SqlMapper.Execute(conn, sql, dic);

总结:

这篇文章只是对Dapper入参进行的分析,出参还没看呢,先这样吧,有空再说.

Dapper源码学习和源码修改的更多相关文章

  1. Dapper源码学习和源码修改(下篇)

    目录: Dapper源码学习和源码修改(上篇主要讲解入参解析) Dapper源码学习和源码修改(下篇主要讲解出参解析) 继上篇讲了下自己学习Dapper的心得之后,下篇也随之而来,上篇主要讲的入参解析 ...

  2. Spring的学习和源码的学习

    PS:Spring中有各种的Templeate,比如jdncTemplate,主要是为了避免各种模板的代码,抽象出来的 PS: @Configration.@Bean是用来替代xml那种解析方式 PS ...

  3. Unsafe 学习和源码阅读

    在代码中获取 Unsafe 对象的方法: // 在 AtomicInteger 里面是这么用的private static final Unsafe unsafe = Unsafe.getUnsafe ...

  4. Java学习-039-源码 jar 包的二次开发扩展实例(源码修改)

    最近在使用已有的一些 jar 包时,发现有些 jar 包中的一些方法无法满足自己的一些需求,例如返回固定的格式,字符串处理等等,因而需要对原有 jar 文件中对应的 class 文件进行二次开发扩展, ...

  5. ABP框架源码学习之修改默认数据库表前缀或表名称

    ABP框架源码学习之修改默认数据库表前缀或表名称 1,源码 namespace Abp.Zero.EntityFramework { /// <summary> /// Extension ...

  6. Java集合专题总结(1):HashMap 和 HashTable 源码学习和面试总结

    2017年的秋招彻底结束了,感觉Java上面的最常见的集合相关的问题就是hash--系列和一些常用并发集合和队列,堆等结合算法一起考察,不完全统计,本人经历:先后百度.唯品会.58同城.新浪微博.趣分 ...

  7. 【iScroll源码学习00】模拟iScroll

    前言 相信对移动端有了解的朋友对iScroll这个库非常熟悉吧,今天我们就来说下我们移动页面的iScroll化 iScroll是我们必学框架之一,我们这次先根据iScroll功能自己实现其功能,然后再 ...

  8. 【mybatis源码学习】mybtias知识点

    Mybatis技术内幕系列博客,从原理和源码角度,介绍了其内部实现细节,无论是写的好与不好,我确实是用心写了,由于并不是介绍如何使用Mybatis的文章,所以,一些参数使用细节略掉了,我们的目标是介绍 ...

  9. 深入Apache NiFi 之源码学习

    前言 要问 Hortonworks 这家公司最有产品力的产品是什么,我觉得是 Apache NiFi.去年Cloudera 和 Hortonworks 合并之后,以 Cloudera 为主,两家公司进 ...

随机推荐

  1. Jenkins的新建job和配置job

    这里,我们说一下如何新建并且配置一个job,Jenkins的工作其实有很多都是靠job来完成的,job有很多的功能,这里我们只介绍如何新建和配置一个建构项目的job. 新建job          新 ...

  2. C++STL笔记

    C++STL 1.vector 向量,长度可变的数组 头文件 #include<vector> 1.1vector的定义 vector<typename> name; 例如: ...

  3. Mybatis实战之TypeHandler高级进阶

    上篇文章分享了在项目实战中自定义Mybatis的TypeHandler来处理枚举类型.文章结尾也指出了美中不足之处,那就是每次都需要指定我们自定义的枚举TypeHandler. 随着项目枚举类型的增多 ...

  4. SaberRD之蒙特卡罗分析(一)

    [声明]本博文的大部分内容摘录于网络,本人按照自己的思维习惯和文字风格进行了重新整理以便于理解和记忆. 鉴于篇幅,我打算先对蒙特卡罗分析的基本思想和历史渊源做一下简单的梳理,然后在下一篇博文中介绍Sa ...

  5. 每天一个linux命令(38)--lsof 之FD文件描述符

    一般lsof 会输出以下这些信息: COMMAND: 进程的名称 PID:进程标识符 PPID:父进程标识符(需要指定-R参数) USER:进程所有者 PGID:进程所属组 FD:文件描述符,应用程序 ...

  6. nginx负载均衡2

    负载均衡2 网站是发展初期,nginx只代理了后端一台服务器,但由于网站名气大涨访问的人越来越多一台服务器实在是顶不住,于是我们加了多台服务器,那么多台服务器又怎么配置代理呢,这里以两台服务器为案例, ...

  7. 如何用一张图片代替 'input:file' 上传本地文件??

    今天去面试,碰到了一道题,也许是因为紧张或者喝水喝多了,一时竟然没有转过弯来,回来之后一细想原来这么简单,哭笑不得,特此记录一下! 原题是这样的:  如何用一张图片代替 'input:file' 上传 ...

  8. C#语言基础——语句

    1·语句是指程序命令,都是按照顺序执行的.语句在程序中的执行顺序称为"控制流"或"执行流".根据程序对运行时所收到的输入的响应,在程序每次执行时控制流可能有所不 ...

  9. 使用jQuery快速高效制作网页特效-----------------------------之jQuery事件与动画

    1.基础事件 分为三个事件 1.1 window事件 所谓window事件,就是当用户执行某些会影响浏览器的操作时,而触发的事件. 1.2 鼠标事件 鼠标事件顾名思义就是当用户在文档上移动或单击鼠标时 ...

  10. mfc---获取当前时间

    CTime t = CTime::GetCurrentTime(); CString strTime = t.format("%Y/&m%d" %h:%M:%S);