DataSet & DataTable &DataRow 深刻浅出

 
 年少时,为什么不为自己的愿意去奋斗五回啊?纵使头破血流,也不悔有这年少轻狂。感慨很多,近日事务也很多,博客也很少更新了,毕竟每个人都急需为友好的活着去拼命。

 

 
 近来在一个群里遭遇一个人说的话,在此处不再赘述,大概意思就是团结各种明白各类懂,面试时各样装逼各类吊,本人真诚的求教了一下他,问她是不是懂这一个事物的底部原理,是否精通过底层源码,能否依据实际情状修改源码,何人知被她吐槽说装逼,说知识那么多无法如何都看源码和清楚原理吧。可是我只想说,这但是你协调说自己精通,难道领会的框架不该了然源码和原理吗?难道领悟就是只知道怎么概括的应用吗?难道是自家拉家常的法子不对?

 

 
 近来赶上一个题目,这就是关于Dapper.NET的有些问题,Dapper.NET的频率为什么很高?该器件的运作规律是哪些?说句实话,我找了很久都不曾察觉接近的稿子,不清楚是不是自我的搜素情势不对,还期待发现接近好的篇章的情人发给我看看,知识在于分享嘛,不要吝啬你的知识,让大家一同前进吧。

本篇作品适合有自然的基础的人去查看 ,最好读书过一定net
编程基础在来查阅此作品。

   在这里大概介绍一下其原理  

1.概念

  DataSetADO.NET的骨干概念。能够把DataSet当成内存中的数据库,DataSet是不依赖于数据库的独自数据集合。所谓独立,就是说,尽管断开数量链路,或者关闭数据库,DataSet依然是可用的,DataSet在其中是用XML来讲述数据的,由于XML是一种与平台无关、与语言无关的多少描述语言,而且可以描述复杂关系的数量,比如父子关系的数目,所以DataSet实际上可以包容所有复杂关系的数码,而且不再依靠于数据库链路。大家得以把DataSet当成内存数据库,DataSet里面可以储存多个表(DataTable);我们称DataSet为数据集对象。

  DataTable是一个临时保存数据的网格虚拟表(表示内存中数据的一个表。)。DataTable是ADO
dot net 库中的大旨目标。它可以被拔取在 VB 和 ASP
上。它并非代码就可以概括的绑定数据库。它拥有微软作风的用户界面。

其余使用DataTable的靶子包括DataSet和DataView,DataTable中存放的是一行行的数据,这一行行的数据就是DataRow
的数组(Array);我们称DataTable 为数据表对象。 

       DataRow是DataTable中的数据行,他里面有隐含两个数据列,每个列能够储存不同品种的值。

 

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

2.DataSet 的广阔使用 

在C# 中 DataSet 存在于 System.Data
命名空间下,类似于java中包的不二法门。DataSet 有两种事列情势,代码如下

using System;
using System.Data;

namespace testData
{
    class Program
    {
        static void Main(string[] args)
        {
            DataSet ds1 = new DataSet();//无构造实例,不指定DataSetName
            Console.WriteLine(ds1.DataSetName);
            DataSet ds2 = new DataSet("MySet");//一个构造实例,指定DataSetName
            Console.WriteLine(ds2.DataSetName);
            Console.ReadKey();
        }
    }
}

语言 1

DataSet 常用的特性就一个,Tables ,这里就只是多解释了,如下列出DataSet
.Tables 如下常用属性:

//
ds.Tables.Count;//获取数据集中存在的表的个数
//
ds.Tables.Add(new DataTable());//添加一个表(DataTable)到数据集

//DataTable[] array = { new DataTable(), new DataTable(), new
DataTable() };

//ds.Tables.AddRange(array);//添加一个表(DataTable)的数组(Array)到数据集

//ds.Tables.Remove();//删除一个表

//ds.Tables.RemoveAt();//依照表的目录从数据集删除一个表

//ds.WriteXml()//将xml 文件写入到DataSet 数据集中

//ds.ReadXml()//将一个xml 文件读取到数量汇总

//ds.Tables.CanRemove()//验证是否可以去除一个成团中的对象。

二.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反射IDataReader的体系队列,来很快的取得和暴发对象。GetInit()方法是一个静态方法,该格局的“Type
commandType”参数表示连接关联的Command对象,再次回到一个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流大小为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()获取缓存新闻。

 3.DataTable 的广泛使用

 DataTable 和DataSet
命名空间一样,实例化的点子有三种,不过常用的就二种,第两种指定了表空间;这里可是多解释,现在大家来看下二种常用实例形式;

语言,实例化1                               DataTable dt0 = new
DataTable();//没有点名表名,默认表名为 NewDataTable
实例化2                               DataTable dt1 = new
DataTable(“User”);//指定表名

using System;
using System.Data;

namespace testData
{
    class Program
    {
        static void Main(string[] args)
        {
            DataSet ds = new DataSet("Set");
            DataTable dt = new DataTable("User");
            ds.Tables.Add(dt);//把一个表加入到数据集中
            Console.WriteLine(ds.Tables.Count);
            Console.WriteLine(ds.Tables[0].TableName);
            Console.Read();
        }
    }
}

  通过上边的代码,咱们可以见见,大家拔取DataSet
的Add方法将一个DataTable 参与数据集。

在前文概述中,我们就说了DataTable
是一个数据表,数据表就要有表头,那么一旦创制一个表头呢,上代码

 DataSet ds = new DataSet("Set");
            DataTable dt = new DataTable("User");
            dt.Columns.Add(new DataColumn("ID", typeof(int)));//设定表头ID
            dt.Columns.Add("Name", typeof(string));//设定表头Name
            dt.Columns.Add("Age", Type.GetType("System.Int32"));//设定表头Name
            for (int i = 0; i < dt.Columns.Count; i++)
            {
                Console.WriteLine("遍历表头方式1_____"+dt.Columns[i]);

            }
            foreach (var item in dt.Columns)
            {
                Console.WriteLine("遍历表头方式2~~~" + item);
            }
            ds.Tables.Add(dt);//把一个表加入到数据集中
            Console.WriteLine("当前数据集中有“"+ ds.Tables.Count + "”个表   ");
            Console.WriteLine("当前数据集的表名   " + ds.Tables[0].TableName);
            Console.Read();

 这样一张空的表格,我们就有了,下面就是大家什么添加一个多少行了,别急,我们先来看下DataRow的运用。关于成立一个数据行我们会在高级部分解释。

三.Dapper.NET扩展:

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

 
 Dapper.NET扩张方法包

    Dapper包

 4.DataRow 的大面积使用

DataRow 的创导模式,只有一种,因为大家net
中设定了DataRow不容许实例化,只好通过
DataTable.NewRow()来创建,如 DataRow dr =
dt.NewRow();那样大家就赢得了一个空行对象。

4.1 怎么样给DataTable 增加行数据吧??看如下代码

using System;
using System.Data;

namespace testData
{
    class Program
    {
        static void Main(string[] args)
        {
            DataSet ds = new DataSet("Set");
            DataTable dt = new DataTable("User");
            dt.Columns.Add(new DataColumn("ID", typeof(int)));//设定表头ID
            dt.Columns.Add("Name", typeof(string));//设定表头Name
            dt.Columns.Add("Age", Type.GetType("System.Int32"));//设定表头Name
            for (int i = 0; i < dt.Columns.Count; i++)
            {
                Console.WriteLine("遍历表头方式1_____"+dt.Columns[i]);

            }
            foreach (var item in dt.Columns)
            {
                Console.WriteLine("遍历表头方式2~~~" + item);
            }
            DataRow dr = dt.NewRow();
            dr["ID"] = 1;
            dr["Name"] = "xiaomeng";
            dr["age"] = 21;
            dt.Rows.Add(dr);
            ds.Tables.Add(dt);//把一个表加入到数据集中
            for (int i = 0; i < dt.Rows.Count; i++)
            {
                DataRow item= dt.Rows[i];
                Console.WriteLine(item["name"]+"今年"+item["age"]+"岁了");
            }
            Console.WriteLine("当前数据集中有“"+ ds.Tables.Count + "”个表   ");
            Console.WriteLine("当前数据集的表名   " + ds.Tables[0].TableName);
            Console.Read();
        }
    }
}

咱俩很多程序员在开发的时候,都会认为这就把一个数额行添加到DataTable
里面了,其实不是如此的?具体看高级部分

 

四.总结:

   
这篇博文是我硬着头皮写的,因为基本没有类似的稿子,连参考的素材都尚未,最多的就是调用代码的demo,对于原理和底部源码解析基本没有,在这里就用这篇博文引出大神对其系数的辨析。希望对我们有一些扶植,也算是尽力了。

5.归结使用之CRUD(增删改查)

5.1新增 

大家在DataRow中是有一个特性叫RowState
叫做行状态,行状态紧要有如下值:“UnChange”(无变化),“Added”(新增过后的数据),“Modified”(修改将来的数量),“Deleted”
删除中的数据,如下图,下图为地点添加行数据的代码,断点调试。

语言 2

透过上诉分析,我们会发觉,添加的数量,并没有提交到内存上,只是程序临时存储的。那么大家怎么提交呢,看如下代码。

using System;
using System.Data;

namespace testData
{
    class Program
    {
        static void Main(string[] args)
        {
            DataSet ds = new DataSet("Set");
            DataTable dt = new DataTable("User");
            dt.Columns.Add(new DataColumn("ID", typeof(int)));//设定表头ID
            dt.Columns.Add("Name", typeof(string));//设定表头Name
            dt.Columns.Add("Age", Type.GetType("System.Int32"));//设定表头Name
            for (int i = 0; i < dt.Columns.Count; i++)
            {
                Console.WriteLine("遍历表头方式1_____"+dt.Columns[i]);

            }
            foreach (var item in dt.Columns)
            {
                Console.WriteLine("遍历表头方式2~~~" + item);
            }
            DataRow dr = dt.NewRow();
            dr["ID"] = 1;
            dr["Name"] = "xiaomeng";
            dr["age"] = 21;
            dt.Rows.Add(dr);
            dt.AcceptChanges();//提交数据
            ds.Tables.Add(dt);//把一个表加入到数据集中
            for (int i = 0; i < dt.Rows.Count; i++)
            {
                DataRow item= dt.Rows[i];
                Console.WriteLine(item["name"]+"今年"+item["age"]+"岁了");
            }
            Console.WriteLine("当前数据集中有“"+ ds.Tables.Count + "”个表   ");
            Console.WriteLine("当前数据集的表名   " + ds.Tables[0].TableName);
            Console.Read();
        }
    }
}

  语言 3

 

 提交代码DataTable.AcceptChanges();那么有内存的交付是不是也应当有内存的回滚,这里不过多介绍了,大家来看下

DataTable 新增行数据的时候状态 为 Added
,这多少个时候可以运用DataTable的 .AcceptChanges()方法举办付出,可以行使RejectChanges()函数举办回滚。

DataTable 修改行数据的时候状态 为
Modified ,这一个时候可以应用DataTable的 .AcceptChanges()方法举行提交,能够利用RejectChanges()函数进行回滚。

DataTable 删除行数据的时候状态 为
Deleted,这个时候能够采用DataTable的 .AcceptChanges()方法开展提交,可以使用RejectChanges()函数举行回滚。

下边来看下增删改的代码

using System;
using System.Data;

namespace testData
{
    class Program
    {
        static void Main(string[] args)
        {
            DataTable dt = new DataTable();
            DataColumn[] arrayColumn = {
                new DataColumn("id", typeof(int)),
                new DataColumn("name", typeof(string)),
                new DataColumn("age", typeof(int)),
                new DataColumn("sex", typeof(string))
            };  
            dt.Columns.AddRange(arrayColumn);
            Console.WriteLine("---------------------------新增---------------------------");
            DataRow dr0 = null;
            for (int i = 0; i < 5; i++)
            {
                dr0 = dt.NewRow();
                dr0[0] = i;
                dr0[1] = "xiaoming"+i;
                dr0[2] = 20+i+new Random().Next(1,10);
                dr0[3] = new Random().Next(i, 200)%3==0?"男":"女";

                dt.Rows.Add(dr0);
            }
            Console.WriteLine("新增没有提交打印");
            Print(dt);
            Console.WriteLine("状态==================" + dr0.RowState);
            dt.AcceptChanges();//新增提交
            Console.WriteLine("新增提交后打印");
            Print(dt);
            Console.WriteLine("状态==================" + dr0.RowState);
            Console.WriteLine("---------------------------修改---------------------------");
            DataRow dr1 = dt.Rows[0];
            dr1["name"] = "wbcsky";
            Console.WriteLine("修改没有提交打印");
            Print(dt);
            Console.WriteLine("状态==================" + dr1.RowState);
            dt.AcceptChanges();//新增提交
            Console.WriteLine("修改提交后打印");
            Print(dt);
            Console.WriteLine("状态==================" + dr1.RowState);
            Console.WriteLine("---------------------------删除---------------------------");
            DataRow dr2=dt.Rows[0]  ;//删除第一条
            dr2.Delete();//这里没有使用dt.rmove 和dt.rmoveat,因为这两个方法直接提交了
            Console.WriteLine("删除没有提交打印");
            Print(dt);
            Console.WriteLine("状态==================" + dr2.RowState);
            dt.AcceptChanges();//新增提交
            Console.WriteLine("删除提交后打印");
            Print(dt);
            Console.WriteLine("状态==================" + dr2.RowState);
            Console.Read();
        }

        private static void Print(DataTable dt)
        {
            foreach (DataRow item in dt.Rows)
            {
                string msg = "";
                try
                {
                    msg = $"id={ item[0]},name={item[1]},age={item[2]},sex={item[3]}";
                }
                catch (Exception e)
                {
                     msg =e.Message;

                }

                Console.WriteLine(msg);
            }
        }
    }
}

举办结果如下图

语言 4

6.归咎应用之筛选排序

咱俩倘诺想给地点的表排序,要怎么排序呢?????,其实排序我们应用DataTable.Select
方法就可以,我们看下怎么采纳

语言 5

大家相会到select
方法有两个重载,我们只用一个参数的和连个参数的,其中一个参数的是赛选,六个参数的首个参数为筛选,第二个为排序

using System;
using System.Data;

namespace testData
{
    class Program
    {
        static void Main(string[] args)
        {
            DataTable dt = new DataTable();
            DataColumn[] arrayColumn = {
                new DataColumn("id", typeof(int)),
                new DataColumn("name", typeof(string)),
                new DataColumn("age", typeof(int)),
                new DataColumn("sex", typeof(string))
            };  
            dt.Columns.AddRange(arrayColumn);

            DataRow dr0 = null;
            for (int i = 0; i < 5; i++)
            {
                dr0 = dt.NewRow();
                dr0[0] = i;
                dr0[1] = "xiaoming"+i;
                dr0[2] = 20+i+new Random().Next(1,5);
                dr0[3] = new Random().Next(i, 200)%3==0?"男":"女";

                dt.Rows.Add(dr0);
            }
            Console.WriteLine("---------------------------筛选之前---------------------------");
            Print(dt);
            DataRow[] rowArr=   dt.Select("age >25", " age desc");
            Console.WriteLine("---------------------------筛选之后按年龄排序---------------------------");
            PrintRow(rowArr);
            Console.Read();
        }
        private static void PrintRow(DataRow[] rowArr)
        {
            foreach (DataRow item in rowArr)
            {
                string msg = "";
                try
                {
                    msg = $"id={ item[0]},name={item[1]},age={item[2]},sex={item[3]}";
                }
                catch (Exception e)
                {
                    msg = e.Message;

                }

                Console.WriteLine(msg);
            }
        }
        private static void Print(DataTable dt)
        {
            foreach (DataRow item in dt.Rows)
            {
                string msg = "";
                try
                {
                    msg = $"id={ item[0]},name={item[1]},age={item[2]},sex={item[3]}";
                }
                catch (Exception e)
                {
                     msg =e.Message;

                }

                Console.WriteLine(msg);
            }
        }
    }
}

 

发表评论

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

网站地图xml地图