关于Dapper.NET的相关论述

年少时,为何不为自己的梦想去拼搏一次呢?纵使头破血流,也不悔有那年少轻狂。感慨很多,最近事情也很多,博客也很少更新了,毕竟每个人都需要为自己的生活去努力。

最近在一个群里遇到一个人说的话,在这里不再赘述,大概意思就是自己各种精通各种懂,面试时各种装逼各种吊,本人真诚的求教了一下他,问他是否懂这些东西的底层原理,是否了解过底层源码,能否根据实际情况修改源码,谁知被他吐槽说装逼,说知识那么多不能什么都看源码和理解原理吧。但是我只想说,这可是你自己说自己精通,难道精通的框架不该了解源码和原理吗?难道精通就是只知道怎么简单的应用吗?难道是我聊天的方式不对?

最近遇到一个问题,那就是有关Dapper.NET的一些问题,Dapper.NET的效率为何很高?该组件的运行原理是什么?说句实话,我找了很久都没有发现类似的文章,不知道是不是我的搜素方式不对,还希望发现类似好的文章的朋友发给我看看,知识在于分享嘛,不要吝啬你的知识,让我们一起进步吧。

在这里简单介绍一下其原理

一.Dapper.NET概述:

项目开发时,我们都是需要考虑项目的技术架构,尤其是对数据库底层的考虑比较多。现在对于数据库的访问有ADO.NET,EF,Dapper.NET等等,不同的情况会有不同的选择,讨论的时候都会说到“xx很牛逼,xx效率很高”等等,总之需要干一场,才算我们开过会。(很多时候,在开会前项目选什么技术早就定了,但是不开个会就显得做事不严谨...),在选用Dapper.NET时,有人说到Dapper.NET效率高,很牛逼,也不知道那个新人说了一句“为什么Dapper.NET效率高?”

好尴尬...

Dapper.NET是一个简单的ORM,专门从SQL查询结果中快速生成对象。Dapper.Net支持执行sql查询并将其结果映射到强类型列表或动态对象列表。Dapper.Net缓存每个查询的信息。这种全面的缓存有助于从大约两倍于LINQ到SQL的查询生成对象。当前缓存由两个ConcurrentDictionary对象处理,它们从不被清除。

Dapper.Net通过扩展方法将两个映射函数添加到IDbConnection接口,这两个函数都命名为ExecuteMapperQuery。第一个映射结果是一个强类型列表,而第二个映射结果是一个动态对象列表。ExecuteMapperCommand执行并且不返回结果集。所有三个方法都将参数接受为匿名类,其中属性值映射到同名的SQL参数。

Dapper.Net旨在仅处理结果集到对象映射。它不处理对象之间的关系,它不会自动生成任何类型的SQL查询。

二.Dapper.NET原理浅析:

通过Dapper.NET的源码我们可以发现其主要是“分部方法和分部类”,有关于“分部方法和分部类”的知识可以看这篇博客:http://www.cnblogs.com/pengze0902/p/6369541.html。Dapper.Net也假定连接已打开并准备就绪,Dapper.NET通过对IDbConnection接口进行扩展。在Dapper.NET对数据库连接完成后,可以进行相关的操作,接下来我们就来看一下这些操作的实现方式。

1.Query()方法:

Query(thisIDbConnection cnn,stringsql,objectparam =null,

IDbTransaction transaction =null,boolbuffered =true,int? commandTimeout =null, CommandType? commandType =null

改方法表示执行查询,返回按T输入的数据。该方法是Query()方法的泛型方法,有7个参数,第一个参数为IDbConnection扩展类,表示对IDbConnection接口进行扩展,该方法使用了可选参数,提高方法的扩展性。在Query方法的实现中,有一个CommandDefinition类,用来表示sql操作的关键方面。在该类下有一个GetInit()方法。

2.GetInit()方法:

我们都知道Dapper.NET通过Emit反射IDataReader的序列队列,来快速的得到和产生对象。GetInit()方法是一个静态方法,该方法的“Type commandType”参数表示连接关联的Command对象,返回一个Action委托。

我们就具体看一下是如何通过Emit反射IDataReader的序列队列的。

if(SqlMapper.Link>.TryGet(commandInitCache, commandType,outaction)){returnaction; }

Link是一个泛型分部类,这是一个微缓存,查看是否存在一个Action的委托。

varbindByName = GetBasicPropertySetter(commandType,"BindByName",typeof(bool));varinitialLongFetchSize = GetBasicPropertySetter(commandType,"InitialLONGFetchSize",typeof(int));

以上两个操作主要获取BindByName和InitialLONGFetchSize的获取基本属性设置。

if(bindByName !=null|| initialLongFetchSize !=null)

{varmethod =newDynamicMethod(commandType.Name +"_init",null,newType[] {typeof(IDbCommand) });varil =method.GetILGenerator();if(bindByName !=null)

{il.Emit(OpCodes.Ldarg_0);

il.Emit(OpCodes.Castclass, commandType);

il.Emit(OpCodes.Ldc_I4_1);

il.EmitCall(OpCodes.Callvirt, bindByName,null);

}if(initialLongFetchSize !=null)

{il.Emit(OpCodes.Ldarg_0);

il.Emit(OpCodes.Castclass, commandType);

il.Emit(OpCodes.Ldc_I4_M1);

il.EmitCall(OpCodes.Callvirt, initialLongFetchSize,null);

}

il.Emit(OpCodes.Ret);

action= (Action)method.CreateDelegate(typeof(Action));

}

这一步是该操作的核心部分,利用Emit反射操作。根据上一步获取的对应名称的基本属性设置,采用DynamicMethod对象,定义和表示一个可以编译,执行和丢弃的动态方法。丢弃的方法可用于垃圾回收。调用该对象的GetILGenerator方法,返回方法的Microsoft中间语言(MSIL)生成器,默认的MSIL流大小为64字节。判断基本属性设置不为空后,调用ILGenerator类的Emit方法,Emit()将指定的指令放在指令流上,该方法接收一个IL流。EmitCall()将

call

callvirt

指令置于 Microsoft 中间语言 (MSIL) 流,以调用

varargs

方法。我们看到OpCodes类,该类描述中间语言 (IL) 指令。CreateDelegate()完成动态方法并创建一个可用于执行它的委托。

通过以上的反射操作构建好对象后,就会接着执行对应的数据库操作。

3.QueryImpl():

privatestaticIEnumerable

QueryImpl(thisIDbConnection cnn, CommandDefinition command, Type effectiveType)

{objectparam =command.Parameters;varidentity =newIdentity(command.CommandText, command.CommandType, cnn, effectiveType, param ==null?null: param.GetType(),null);varinfo =GetCacheInfo(identity, param, command.AddToCache);

IDbCommand cmd=null;

IDataReader reader=null;boolwasClosed = cnn.State ==ConnectionState.Closed;try{

cmd=command.SetupCommand(cnn, info.ParamReader);if(wasClosed) cnn.Open();

reader= cmd.ExecuteReader(wasClosed ? CommandBehavior.CloseConnection |CommandBehavior.SequentialAccess : CommandBehavior.SequentialAccess);

wasClosed=false;vartuple =info.Deserializer;inthash =GetColumnHash(reader);if(tuple.Func ==null|| tuple.Hash !=hash)

{if(reader.FieldCount ==0)yieldbreak;

tuple= info.Deserializer =newDeserializerState(hash, GetDeserializer(effectiveType, reader,0, -1,false));if(command.AddToCache) SetQueryCache(identity, info);

}varfunc =tuple.Func;varconvertToType = Nullable.GetUnderlyingType(effectiveType) ??effectiveType;while(reader.Read())

{objectval =func(reader);if(val ==null|| valisT)

{yieldreturn(T)val;

}else{yieldreturn(T)Convert.ChangeType(val, convertToType, CultureInfo.InvariantCulture);

}

}while(reader.NextResult()) { }

reader.Dispose();

reader=null;

command.OnCompleted();

}finally{if(reader !=null)

{if(!reader.IsClosed)try{ cmd.Cancel(); }catch{/*don't spoil the existing exception*/}

reader.Dispose();

}if(wasClosed) cnn.Close();if(cmd !=null) cmd.Dispose();

}

}

该方法为执行查询操作的核心方法,通过CommandDefinition类的相关操作后,获取到相应的对象后,执行这一步操作。该方法是IDbConnection的扩展方法,CommandDefinition表示sql的相关操作对象,Type表示传入的一个有效的类型。Identity对象表示Dapper中的缓存查询的标识,该类是一个分部类,可以对其进行相应的扩展。GetCacheInfo()获取缓存信息。

三.Dapper.NET扩展:

这一部分是借花献佛,该部分代码是对Dapper.NET代码做一封装,可以类似于操作其他ORM的方式,需要者可以自取,就不要到处去找这些东西了。

Dapper.NET扩展方法包

Dapper包

四.总结:

这篇博文是我硬着头皮写的,因为基本没有类似的文章,连参考的资料都没有,最多的就是调用代码的demo,对于原理和底层源码解析基本没有,在这里就用这篇博文引出大神对其全面的解析。希望对大家有一点帮助,也算是尽力了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,779评论 18 399
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,997评论 19 139
  • 一. Java基础部分.................................................
    wy_sure阅读 3,839评论 0 11
  • 作者:Benjamin H. Sigelman, Luiz Andr´e Barroso, Mike Burrow...
    Josh_Song阅读 1,266评论 0 8
  • 今天白天,我在微博上发了好几条关于“小彩旗在春晚的四小时旋转是不是当代艺术”的讨论,貌似很少人搭理呀,而且少数搭理...
    雕爷阅读 2,091评论 3 8