生活

生活DotNet友元程序集解析

24 12月 , 2018  

 
 项目支出的经过中,调试使用的或者是最多的操作。任何代码写出来都亟待经过调试和烧结,以此扩展和擢升程序的安定和可靠性。谈到.NET的单元测试,在这边就得提提.NET的友元程序集这一特征,也借用.NET举办单元测试的一个较为好用的.NET属性,来上课一下程序集、定制Attribute的连锁知识。一些文化需要频繁的去品味和反省,不要认为您会了就大意,等你注意的时候,你就有些力不从心的意思了。

 
 年少时,为何不为自己的愿意去拼搏三遍啊?纵使头破血流,也不悔有这年少轻狂。感慨很多,近期工作也很多,博客也很少更新了,毕竟每个人都亟待为和谐的活着去拼命。

   生活在于不停的磨难,唯有由此磨练,才可知几时需要安分,哪一天需要搦战。

 
 最近在一个群里境遇一个人说的话,在此地不再赘言,大概意思就是祥和各类精晓各个懂,面试时各个装逼各个吊,本人真诚的求教了刹那间她,问她是否懂这个东西的平底原理,是否领悟过底层源码,能否按照实际意况修改源码,谁知被他吐槽说装逼,说知识那么多无法如何都看源码和领会原理吧。但是自己只想说,这可是你协调说自己通晓,难道了解的框架不该领会源码和公理吗?难道领会就是只精通怎么概括的采取吗?难道是我拉家常的办法不对?

   毒鸡汤喝完了,来聊聊正事…

 
 近年来遇见一个题目,这就是关于Dapper.NET的部分题目,Dapper.NET的效用为啥很高?该零件的运作规律是怎样?说句实话,我找了很久都未曾意识类似的篇章,不知情是不是本身的搜素情势不对,还指望发现类似好的著作的对象发给自己看看,知识在于分享嘛,不要吝啬你的文化,让我们一块发展呢。

一.程序集概述

 
本文首如果座谈“友元程序集”的一对知识,既然是做一个剖析,那么就应有把一些文化做一个拓展来阐释。在此处先谈谈程序集(有人认为很了然,有人认为完全不懂,情况各异,采取不同,需者自取吧),接下去大家具体的探视程序集这一特征。

 
程序集是一个或四个模块/资源文件的逻辑分组,程序集是重用、安全性以及版本控制的纤维单元。对于程序集的社团有如下图。

生活 1

生活 2

   
对于程序集的组成就不一一做分析,在这里就独自谈谈元数据这一构造。元数据是一个二进制数据块,由一组数据表,元数据连接与富含IL代码的公文涉及,元数据由多少个表组成。元数据的职能有上图介绍。元数据的表有两个项目:定义表,引用表,清单表。对于这一个表的社团在这边就不做牵线了,有趣味的可以搜索一下,个人认为元数据这一结构应该好好的研讨一下。

   在这边大概介绍一下其规律  

二.定制Attribute概述

 
 下边的论述中简易的介绍了先后集的构造和元数据,在此地大概的介绍一下定制Attribute这一.NET的特点。定制Attribute允许定义的音信应用于几乎每一个元数据表记录项,这种可扩展的元数据音信能在运转时查询,从而动态改变代码的实施措施。在C#种,为了将一个定制Attribute应用于一个对象元素,需要将Attribute放置于目的元素前边的一对方括号中。

 
 CLR允许将定制Attribute应用于可在文件的元数据中象征的几乎所有因素。定制Attribute重要行使于程序集、模块、类型、字段、方法、方法参数、方法重回值、属性、事件、泛型类型参数。attribute是类的一个实例,将一个attribute应用于一个目的元素时,语法类似于调用类的某个实例构造函数。定制Attribute的实例如下:

[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true, Inherited = false)]

   
下边代码取自InternalsVisibleToAttribute类中,该类是做到友元程序集特性的中央目标,上边会做一个有血有肉的介绍。AttributeUsage需要承受一个AttributeTargets枚举类型的值作为参数,称之为定位参数,是强制性的,必须指定。AllowMultiple参数用于获取或设置一个布尔值,提示是否有三个实例指定的性质可以为单个程序元素指定。Inherited参数用于获取或设置一个布尔值,指示指定的特性是否能够延续由派生类和重写成员。

 
 定制Attribute可以使用于单个目的元素,定制Attribute的依次是可有可无的。在C#种,可将每个Attribute都封闭到一对方括号中,也可以在有些方括号中查封三个以逗号分隔的Attribute。

 
 定制Attribute就是一个类的实例,它被体系化成驻留在元数据中的一个字节流,在运转时,可以对元数据中含有的字节举办反连串化,从而构造类的一个实例。

一.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查询。

三.友元程序集解析

 
 扯了半天,终于到讲师“友元程序集”那些概念,“友元程序集”在.NET2中指出,使用InternalsVisibleToAttribute来实现这一特征,InternalsVisibleTo只好用于程序集,并且你可以在同一个程序集种应用多次。源程序集:包含这么些特性的先后集。友元程序集可以看到源程序集的拥有内部成员,类似于国有的。

   友元程序集实例介绍:

//AssemblySource.DLL
[assembly: InternalsVisibleTo(DotNetShare)]
public class AssemblySource
{
    public static void Share();
}


//DotNetShare.DLL
public class DotNetShare
{
    private static void Main()
    {
        AssemblySource.Share();
    }
}

   
AssemblySource.DLL和DotNetShare.DLL之间存在一种非常的涉及,可是这种关涉只好单项操作。接下来看一下InternalsVisibleToAttribute的落实源码。InternalsVisibleToAttribute继承自Attribute类,该类指定平时仅在脚下先后集中可见的项目对点名程序集可见。该类包含五个属性和一个主意。

   1.AssemblyName

public string AssemblyName
    {
      [__DynamicallyInvokable] get
      {
        return this._assemblyName;
      }
    }

 
 该属性为一个只读属性,一个意味着友元程序集名称的字符串。该属性用于获取友元程序集的称谓,选拔internal 关键字标记的持有项目和序列成员对该程序集均为可见。

  2.InternalsVisibleToAttribute()

public InternalsVisibleToAttribute(string assemblyName)
    {
      this._assemblyName = assemblyName;
    }

   该措施为一个构造函数,用指定的友元程序集的称呼初始化 <see
cref=”T:System.Runtime.CompilerServices.InternalsVisibleToAttribute”/>
类的新实例。接收一个友元程序集的名目。

 
对于友元程序集有一个束缚,假设一个友元程序集是签约的,那么源程序集为了保险信任正确的代码,就需要指定友元程序集的公钥。

二.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()方法的泛型方法,有7个参数,首个参数为IDbConnection扩张类,表示对IDbConnection接口举行扩大,该格局运用了可选参数,提高措施的扩大性。在Query方法的兑现中,有一个CommandDefinition类,用来代表sql操作的最紧要方面。在此类下有一个GetInit()方法。

   2.GetInit()方法:

   
我们都了解Dapper.NET通过Emit反射IData里德(Reade)r的行列队列,来快速的收获和发生对象。GetInit()方法是一个静态方法,该办法的“Type
commandType”参数表示连接关联的Command对象,再次回到一个Action<IDbCommand>委托。

   大家就实际看一下是何许通过Emit反射IData里德r的队列队列的。

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流大小为64字节。判断基本属性设置不为空后,调用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中的缓存查询的标识,该类是一个分部类,可以对其开展相应的扩张。GetCacheInfo()获取缓存消息。

四.总结

 
 对于本文紧假若在介绍友元程序集这一表征,顺带介绍程序集和定制Attribute这六个性状,方便大家清楚友元程序集这一特点。这篇作品希望对我们享有帮助,仍然那句话,需者自取,也虚心接受吐槽。知识在于分享,更在乎每一个人的构思。

 

三.Dapper.NET扩展:

 
 这一有些是借花献佛,该片段代码是对Dapper.NET代码做一封装,能够接近于操作其他ORM的法门,需要者可以自取,就不要到处去找这一个东西了。

 
 Dapper.NET扩充方法包

    Dapper包

四.总结:

   
那篇博文是我硬着头皮写的,因为基本没有接近的作品,连参考的材料都尚未,最多的就是调用代码的demo,对于原理和底部源码解析基本没有,在这里就用这篇博文引出大神对其系数的分析。希望对大家有好几帮衬,也好不容易尽力了。


相关文章

发表评论

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

网站地图xml地图