NET的连锁论述

by admin on 2019年4月14日

 
 年少时,为什么不为自身的希望去拼搏1次啊?纵使人仰马翻,也不悔有那一年少轻狂。感慨很多,近日工作也很多,博客也很少更新了,终归每种人都亟需为友好的活着去拼命。

 
 年少时,为啥不为本身的希望去努力一回啊?纵使风声鹤唳,也不悔有今年少轻狂。感慨很多,目前工作也很多,博客也很少更新了,毕竟各种人都亟需为自个儿的活着去拼命。

 
 近日在多少个群里遭遇1人说的话,在这边不再赘言,大致意思就是温馨各样精晓各类懂,面试时各种装逼各样吊,本身真诚的求教了一下他,问他是或不是懂那个事物的尾部原理,是还是不是了解过底层源码,能还是不可能依照真实情状修改源码,何人知被他捉弄说装逼,说知识那么多不可能怎样都看源码和精通原理吧。可是自身只想说,那可是你协调说自身理解,难道明白的框架不应当掌握源码和规律吗?难道明白正是只通晓怎么归纳的运用吗?难道是自笔者拉家常的措施不对?

 
 近日在3个群里境遇一人说的话,在这边不再赘言,大概意思正是友善各个精晓各类懂,面试时各样装逼种种吊,自个儿真诚的求教了须臾间他,问他是或不是懂这么些事物的最底层原理,是还是不是理解过底层源码,能不能依据真实情形修改源码,何人知被他调侃说装逼,说知识那么多不能怎么样都看源码和清楚原理吧。可是本人只想说,那只是您协调说自个儿了解,难道明白的框架不应该了然源码和公理吗?难道明白正是只晓得怎么回顾的采取吗?难道是自个儿拉家常的诀窍不对?

 
 如今遭受二个题材,那就是有关Dapper.NET的有的标题,Dapper.NET的频率为啥很高?该器件的运营原理是何许?说句实话,作者找了很久都并未有察觉接近的稿子,不知道是还是不是自身的搜素方式不对,还盼望发现接近好的小说的对象发给自个儿看看,知识在于分享嘛,不要吝啬你的学问,让我们联合前行呢。

 
 近来遇见三个难点,那便是有关Dapper.NET的有个别标题,Dapper.NET的作用为啥很高?该器件的运维原理是怎么?说句实话,作者找了很久都未有意识类似的篇章,不清楚是还是不是自家的搜素情势不对,还盼望发现类似好的稿子的情人发给作者看看,知识在于分享嘛,不要吝啬你的知识,让我们一同前进啊。

   在此间大约介绍一下其原理  

   在此处差不离介绍一下其原理  

一.Dapper.NET概述:

 
项目开销时,大家都以内需思量项目的技术架构,尤其是对数据库底层的牵记相比多。今后对于数据库的拜访有ADO.NET,EF,Dapper.NET等等,不一致的场所会有例外的抉择,斟酌的时候都会提及“xx很牛逼,xx效能很高”等等,综上可得必要干一场,才算大家开过会。(很多时候,在开会前项目选什么样技艺早已定了,不过不开个会就显得做事不严格…),在选用Dapper.NET时,有人提起Dapper.NET成效高,很牛逼,也不精晓那么些新人说了一句“为啥Dapper.NET功能高?”

   好尴尬…

   Dapper.NET是二个大致的O君越M,专门从SQL查询结果中神速变化对象。Dapper.Net协理实施sql查询并将其结果映射到强类型列表或动态指标列表。Dapper.Net缓存每种查询的音讯。那种全面的缓存有助于从大体上两倍于LINQ到SQL的询问生成对象。当前缓存由三个ConcurrentDictionary对象处理,它们未有被排除。

 
 Dapper.Net通过扩充方法将三个映射函数添加到IDbConnection接口,那八个函数都命名字为ExecuteMapperQuery。第八个映射结果是2个强类型列表,而第二个映射结果是一个动态指标列表。ExecuteMapperCommand实施并且不回来结果集。全数七个点子都将参数接受为匿名类,当中属性值映射到同名的SQL参数。

   Dapper.Net意在仅处理结果集到对象映射。它不处理目的之间的涉嫌,它不会自动生成此外类型的SQL查询。

一.Dapper.NET概述:

 
项目支付时,大家都以急需思量项指标技巧架构,特别是对数据库底层的思索相比较多。未来对此数据库的拜会有ADO.NET,EF,Dapper.NET等等,差别的处境会有例外的取舍,研商的时候都会聊起“xx很牛逼,xx功效很高”等等,总而言之要求干一场,才算大家开过会。(很多时候,在开会前项目选什么技能已经定了,不过不开个会就显得做事不严苛…),在选择Dapper.NET时,有人提起Dapper.NET成效高,很牛逼,也不通晓卓殊新人说了一句“为何Dapper.NET成效高?”

   好尴尬…

   Dapper.NET是贰个不难易行的O陆风X8M,专门从SQL查询结果中飞快转移对象。Dapper.Net援助实施sql查询并将其结果映射到强类型列表或动态指标列表。Dapper.Net缓存每一种查询的新闻。那种周密的缓存有助于从大体上两倍于LINQ到SQL的询问生成对象。当前缓存由多少个ConcurrentDictionary对象处理,它们没有被破除。

 
 Dapper.Net通过扩展方法将多少个映射函数添加到IDbConnection接口,那三个函数都命名字为ExecuteMapperQuery。首个映射结果是三个强类型列表,而第贰个映射结果是一个动态指标列表。ExecuteMapperCommand举行并且不回去结果集。全部多个章程都将参数接受为匿名类,在那之中属性值映射到同名的SQL参数。

   Dapper.Net意在仅处理结果集到指标映射。它不处理对象时期的涉及,它不会自动生成其余项目标SQL查询。

2.Dapper.NET原理分析:

 
 通过Dapper.NET的源码大家得以窥见其首如若“分部方法和分部类”,有关于“分部方法和分部类”的学识能够看那篇博客:http://www.cnblogs.com/pengze0902/p/6369541.html。Dapper.Net也只要连接已打开并预备妥贴,Dapper.NET通过对IDbConnection接口举办扩张。在Dapper.NET对数据库连接形成后,能够开始展览有关的操作,接下去大家就来看一下那一个操作的落到实处形式。

   1.Query()方法:

Query<T>(this IDbConnection cnn, string sql, object param = null, 
         IDbTransaction transaction = null, bool buffered = true, int? commandTimeout = null, CommandType? commandType = null

 
 改方法表示执行查询,重临按T输入的数据。该形式是Query()方法的泛型方法,有七个参数,第2个参数为IDbConnection扩大类,表示对IDbConnection接口举办扩充,该措施应用了可选参数,进步艺术的增添性。在Query方法的落到实处中,有二个CommandDefinition类,用来表示sql操作的重大方面。在此类下有2个GetInit()方法。

   2.GetInit()方法:

   
大家都知情Dapper.NET通过Emit反射IDataReader的行列队列,来连忙的收获和产生对象。GetInit()方法是一个静态方法,该办法的“Type
commandType”参数表示连接关联的Command对象,重回二个Action<IDbCommand>委托。

   大家就具体看一下是怎么样通过Emit反射IData里德r的行列队列的。

if (SqlMapper.Link<Type, Action<IDbCommand>>.TryGet(commandInitCache, commandType, out action)){ return action; }

   Link<TKey,
TValue>是2个泛型分部类,那是三个微缓存,查看是还是不是留存2个Action<IDbCommand>的嘱托。

var bindByName = GetBasicPropertySetter(commandType, "BindByName", typeof(bool));
var initialLongFetchSize = GetBasicPropertySetter(commandType, "InitialLONGFetchSize", typeof(int));

 
 以上五个操作首要取得BindByName和InitialLONGFetchSize的获取基本属性设置。

    if (bindByName != null || initialLongFetchSize != null)
            {
                var method = new DynamicMethod(commandType.Name + "_init", null, new Type[] { typeof(IDbCommand) });
                var il = 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<IDbCommand>)method.CreateDelegate(typeof(Action<IDbCommand>));
            }

 
 这一步是该操作的主干部分,利用Emit反射操作。依据上一步获取的呼应名称的大旨属性设置,选拔DynamicMethod对象,定义和代表2个可以编写翻译,执行和丢弃的动态方法。放任的法子可用于垃圾回收。调用该对象的GetILGenerator方法,重返方法的Microsoft中间语言(MSIL)生成器,暗中认可的MSIL流大小为6肆字节。判断Kit天性设置不为空后,调用ILGenerator类的Emit方法,Emit()将点名的命令放在指令流上,该方式接收2个IL流。EmitCall()将 call 或 callvirt 指令置于
Microsoft 中间语言 (MSIL)
流,以调用varargs 方法。大家来看OpCodes类,该类描述中间语言 (IL)
指令。CreateDelegate()完毕动态方法并创设多少个可用于实施它的委托。

   通过上述的反光操作创设好对象后,就会随之执行相应的数据库操作。

 3.QueryImpl():

 private static IEnumerable<T> QueryImpl<T>(this IDbConnection cnn, CommandDefinition command, Type effectiveType)
        {
            object param = command.Parameters;
            var identity = new Identity(command.CommandText, command.CommandType, cnn, effectiveType, param == null ? null : param.GetType(), null);
            var info = GetCacheInfo(identity, param, command.AddToCache);
            IDbCommand cmd = null;
            IDataReader reader = null;
            bool wasClosed = 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; 
                var tuple = info.Deserializer;
                int hash = GetColumnHash(reader);
                if (tuple.Func == null || tuple.Hash != hash)
                {
                    if (reader.FieldCount == 0) 
                        yield break;
                    tuple = info.Deserializer = new DeserializerState(hash, GetDeserializer(effectiveType, reader, 0, -1, false));
                    if (command.AddToCache) SetQueryCache(identity, info);
                }
                var func = tuple.Func;
                var convertToType = Nullable.GetUnderlyingType(effectiveType) ?? effectiveType;
                while (reader.Read())
                {
                    object val = func(reader);
                    if (val == null || val is T)
                    {
                        yield return (T)val;
                    }
                    else
                    {
                        yield return (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中的缓存查询的标识,该类是1个分部类,能够对其进展对应的壮大。GetCacheInfo()获取缓存消息。

二.Dapper.NET原理分析:

 
 通过Dapper.NET的源码咱们得以窥见其首若是“分部方法和分部类”,有关于“分部方法和分部类”的学问能够看那篇博客:http://www.cnblogs.com/pengze0902/p/6369541.html。Dapper.Net也假使连接已开拓并预备妥贴,Dapper.NET通过对IDbConnection接口实行扩充。在Dapper.NET对数据库连接形成后,可以进行相关的操作,接下去大家就来看一下这个操作的贯彻方式。

   1.Query()方法:

Query<T>(this IDbConnection cnn, string sql, object param = null, 
         IDbTransaction transaction = null, bool buffered = true, int? commandTimeout = null, CommandType? commandType = null

 
 改方法表示执行查询,再次回到按T输入的数据。该措施是Query()方法的泛型方法,有捌个参数,第九个参数为IDbConnection扩大类,表示对IDbConnection接口实行扩展,该办法运用了可选参数,进步措施的扩充性。在Query方法的落实中,有1个CommandDefinition类,用来表示sql操作的重中之重方面。在此类下有三个GetInit()方法。

   2.GetInit()方法:

   
我们都晓得Dapper.NET通过Emit反射IDataReader的行列队列,来快速的获得和爆发对象。GetInit()方法是一个静态方法,该方式的“Type
commandType”参数表示连接关联的Command对象,再次来到3个Action<IDbCommand>委托。

   大家就具体看一下是何等通过Emit反射IDataReader的行列队列的。

if (SqlMapper.Link<Type, Action<IDbCommand>>.TryGet(commandInitCache, commandType, out action)){ return action; }

   Link<TKey,
电视alue>是贰个泛型分部类,那是三个微缓存,查看是不是存在三个Action<IDbCommand>的寄托。

var bindByName = GetBasicPropertySetter(commandType, "BindByName", typeof(bool));
var initialLongFetchSize = GetBasicPropertySetter(commandType, "InitialLONGFetchSize", typeof(int));

 
 以上八个操作首要取得BindByName和InitialLONGFetchSize的得到基特性能设置。

    if (bindByName != null || initialLongFetchSize != null)
            {
                var method = new DynamicMethod(commandType.Name + "_init", null, new Type[] { typeof(IDbCommand) });
                var il = 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<IDbCommand>)method.CreateDelegate(typeof(Action<IDbCommand>));
            }

 
 这一步是该操作的中坚部分,利用Emit反射操作。依照上一步获取的附和名称的骨干质量设置,接纳DynamicMethod对象,定义和表示二个方可编写翻译,执行和抛弃的动态方法。放任的方式可用以垃圾回收。调用该目标的GetILGenerator方法,再次来到方法的Microsoft中间语言(MSIL)生成器,暗许的MSIL流大小为6四字节。判断基性情能设置不为空后,调用ILGenerator类的Emit方法,Emit()将内定的命令放在指令流上,该措施接收三个IL流。EmitCall()将 call 或 callvirt 指令置于
Microsoft 中间语言 (MSIL)
流,以调用varargs 方法。咱们见到OpCodes类,该类描述中间语言 (IL)
指令。CreateDelegate()达成动态方法并创办一个可用以实践它的寄托。

   通过上述的反射操作构建好靶子后,就会跟着执行相应的数据库操作。

 3.QueryImpl():

 private static IEnumerable<T> QueryImpl<T>(this IDbConnection cnn, CommandDefinition command, Type effectiveType)
        {
            object param = command.Parameters;
            var identity = new Identity(command.CommandText, command.CommandType, cnn, effectiveType, param == null ? null : param.GetType(), null);
            var info = GetCacheInfo(identity, param, command.AddToCache);
            IDbCommand cmd = null;
            IDataReader reader = null;
            bool wasClosed = 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; 
                var tuple = info.Deserializer;
                int hash = GetColumnHash(reader);
                if (tuple.Func == null || tuple.Hash != hash)
                {
                    if (reader.FieldCount == 0) 
                        yield break;
                    tuple = info.Deserializer = new DeserializerState(hash, GetDeserializer(effectiveType, reader, 0, -1, false));
                    if (command.AddToCache) SetQueryCache(identity, info);
                }
                var func = tuple.Func;
                var convertToType = Nullable.GetUnderlyingType(effectiveType) ?? effectiveType;
                while (reader.Read())
                {
                    object val = func(reader);
                    if (val == null || val is T)
                    {
                        yield return (T)val;
                    }
                    else
                    {
                        yield return (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中的缓存查询的标识,该类是1个分部类,能够对其展开对应的增添。GetCacheInfo()获取缓存新闻。

三.Dapper.NET扩展:

 
 那一某个是借花献佛,该有的代码是对Dapper.NET代码做一封装,可以接近于操作别的O帕杰罗M的办法,需求者能够自取,就不要随地去找这么些事物了。

 
 Dapper.NET增加方法包

    Dapper包

三.Dapper.NET扩展:

 
 那1有个别是借花献佛,该片段代码是对Dapper.NET代码做1封装,能够接近于操作其余OEscortM的法子,必要者能够自取,就不用随地去找那个东西了。

 
 Dapper.NET扩充方法包

    Dapper包

四.总结:

   
那篇博文是自个儿硬着头皮写的,因为基本未有接近的作品,连参考的材料都未曾,最多的便是调用代码的demo,对于原理和底部源码解析基本未有,在此处就用那篇博文引出大神对其完善的辨析。希望对大家有一些扶植,也终于尽力了。

四.总结:

   
那篇博文是自家硬着头皮写的,因为基本未有像样的文章,连参考的质感都未曾,最多的便是调用代码的demo,对于原理和尾部源码解析基本未有,在此处就用那篇博文引出大神对其周到的分析。希望对大家有1些帮手,也总算尽力了。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图