开发者论坛

 找回密码
 注册 (请使用非IE浏览器)
查看: 5890|回复: 5

[教程] 深入剖析.NET DataTable

[复制链接]

0

精华

5097

贡献

5311

赞扬

管理员

帖子
1154
软币
21035
在线时间
4325 小时
注册时间
2013-6-7

黄马甲

发表于 2013-6-10 23:21:12 | 显示全部楼层 |阅读模式
1、ADO.NET相关对象一句话介绍
1)DataAdapter:
DataAdapter实际是一个SQL语句集合,因为对Database的操作最终需要归结到SQL语句。
2)Dataset:
DataSet可以理解成若干DataTable的集合,DataSet在内存里面维护一个表集合包括表间关系。对于.NET Framework 2.0之前的版本,DataSet在ADO.NET中拥有至关重要的作用,但在其后的版本中,由于DataTable类的完备(例如与XML相关的几个方法以及Merge方法),其作用稍有削弱,甚至于有些情况下你去初始化一个DataSet对象本身就是多余的。
3)DataView:
与数据库中的视图在概念上是类似的。DataView本身并不真正包含数据行,而只是包含指向源DataTable中数据行的引用,这一点你可以通过object.ReferenceEquals()方法来验证。
4)DataTable:
ADO.NET的核心对象。它是位于内存中的一张表,是你执行SQL查询之后的结果集,可以形象地把它理解为一张包含若干行若干列的表格。

2、如何更新数据到Database
从本质上来说,你对Database操作总是归结到SQL语句,但是从表面上我们可以作一点区分,
1)直接使用SQL命令
在.NET中,最常见的是拼接SQL字符串,使用Command对象来执行此命令以达到操作Database的目的,例如,

Code
  1. string sql = "update table1 set fvalue=" + this.textBox1.Text + " where fname='x'";
  2. SqlCommand cmd = new SqlCommand(sql,conn);
  3. cmd.ExecuteNonQuery();
复制代码
这是一种最直接浅显的方式,因为SQL语句就在你眼前,反过来说,这需要你对SQL命令有一定的了解。

2)使用DataAdapter.Update()
另外一种方式,是使用DataAdapter.Update()方法,这并不是说我们不需要SQL语句了,只是SQL语句拼接的工作已经交给了DataAdapter(实际上是交给了CommandBuilder)来完成(以参数的形式),例如,  
Code
  1. string c = "select fname,fvalue from table1";
  2. SqlCommand cmd = new SqlCommand(c,conn);
  3. SqlDataAdapter da = new SqlDataAdapter(cmd);
  4. SqlCommandBuilder scb = new SqlCommandBuilder(da); //(1)
  5. DataTable dt = new DataTable();
  6. da.Fill(dt);
  7. dt.Rows[0].Delete();//(2)
  8. da.Update(dt);
复制代码
在这里,你看不到SQL语句,因为在你初始化SqlCommandBuilder的过程中,将自动根据表结构(基于你的Select语句)构造insert,update,delete语句。对于上面的代码,你可以获得SQL语句内容,
  1. DELETE FROM [table1] WHERE (([fname] = @p1) AND ((@p2 = 1 AND [fvalue] IS NULL) OR ([fvalue] = @p3)))
复制代码
而执行时候,会传入相应的参数值,
  1. exec sp_executesql N'DELETE FROM [table1] WHERE (([fname] = @p1) AND ((@p2 = 1 AND [fvalue] IS NULL) OR ([fvalue] = @p3)))',N'@p1 varchar(1),@p2 int,@p3 int',@p1='a',@p2=0,@p3=100

  2. exec sp_executesql N'DELETE FROM [table1] WHERE (([fname] = @p1) AND ((@p2 = 1 AND [fvalue] IS NULL) OR ([fvalue] = @p3)))',N'@p1 varchar(1),@p2 int,@p3 int',@p1='b',@p2=1,@p3=NULL
复制代码
由于表中只有两个列,列fname为主键列,fvalue列可空,至于为什么会出现三个参数,看看上面的SQL你就会明白了。
以下则分别是update语句、insert语句,
  1. UPDATE [table1] SET [fname] = @p1, [fvalue] = @p2 WHERE (([fname] = @p3) AND ((@p4 = 1 AND [fvalue] IS NULL) OR ([fvalue] = @p5)))
  2. INSERT INTO [table1] ([fname], [fvalue]) VALUES (@p1, @p2)
复制代码
另外,上述C#代码中的dt.Rows[0].Delete()行写在这里只是示例作用,实际的系统中,你可能会有一个叫“Delete”的按钮,这样你可以在按钮的事件中执行Delete()操作,然后叫某个叫“Save”的按钮里写上Update(),这很常见,不多说了。
再另外,由于这些语句的构造过程中依赖于你的Select语句,所以你的Select语句中必须包含主键列,否则无法正常生成其它SQL命令。
以下我们的讨论,将主要针对第二种方式,即使用Update()进行数据更新过程中涉及的各种问题。

3、行状态
为了后续的数据操作,DataTable中引入了一个“行状态”的概念(事实上该属性属于DataRow类)。每一个DataRow都有一个状态标志,你可以通过DataTable.Rows[ i ].RowState查看,对DataRow的不同操作将导致该行处于不同的状态,同时,不同的状态又导致保存数据时的不同行为。

1)初始状态差异
从数据库中查询并通过DataAdapter.Fill()方法填充的DataTable,其所有行的状态初始都为Unchanged(我们可以认为在Fill()方法的内部调用了AcceptChanges()方法),然而对于在程序中手工构造并添加的数据行,在未接受AcceptChanges()方法前,都为Added(行状态的不同在DataTable中是一个比较隐蔽的但又需要十分关注的问题,后续会有相应的说明),参见以下代码。  
Code
  1. private void button1_Click(object sender, EventArgs e)
  2. {
  3.        try
  4.        {
  5.               dataAdapter1.Fill(dt);
  6.               DataRowState s = dt.Rows[0].RowState;//unchanged
  7.        }
  8.        catch
  9.        {
  10.        }
  11. }
复制代码
  1. private void button2_Click(object sender, EventArgs e)
  2. {
  3.        DataTable dt = new DataTable();
  4.        dt.Columns.Add("fname");
  5.        dt.Columns.Add("fvalue");
  6.        dt.Rows.Add("zhang", 100);
  7.        DataRowState s = dt.Rows[0].RowState;//added
  8. }
复制代码

2)理解Delete()

此方法并未真正移除DataRow(除非此行原状态为Added),而只是将RowState状态变成了Deleted(当然这会导致你无法使用正常的索引方式访问此行的数据)。对于Added状态的行执行Delete()操作,将导致DataTable行数减少,这点需要注意,因为它可能导致你在使用for循环遍历时出现索引越界异常。  

3)Exception:Deleted row information cannot be accessed through the row.  
Code
  1. private void button8_Click(object sender, EventArgs e)
  2. {
  3.        DataTable dt = new DataTable();
  4.        dt.Columns.Add("fname");
  5.        dt.Columns.Add("fvalue");
  6.        dt.Rows.Add("zhang", 100);
  7.        //
  8.        dt.AcceptChanges();
  9.        dt.Rows[0].Delete();
  10.        DataRow dr = dt.Rows[0]; //No error
  11.        object o = dt.Rows[0]["fvalue"];//Exception,row can be accessed,but row data cannot
  12. }
复制代码

4)理解AcceptChanges()

此方法容易给人误解,以为在调用它之后对DataTable所做的所有更改将会被提交到Database。事实上,此方法跟Database没有直接的关系(注意),它只直接影响各DataRow的RowState(具体地说来是将所有状态为Deleted的行真正移除,所有状态为Added或Modified的行都变成Unchanged)。与Database有直接相关的是DataAdapter.Update()方法,它是真正负责执行相关SQL命令的地方。
但是,从另一方面来说,没有直接的影响,言外之意就是有间接的影响,由于它影响了所有DataRow的RowState,而DataAdapter.Update()方法在执行SQL命令时必须依据RowState以确定使用insert、update、或delete 命令。举个例子,如果你在DataAdapter.Update()调用之前执行AcceptChanges()方法,这将阻止所有对Database的更改,因此对这两个方法调用的顺序应有充分的考虑。
另外,DataSet、DataTable、DataRow都有AcceptChanges()方法,这些方法除了影响的范围大小不同之外,没有本质的区别。

5)DataRowState与Update()
不同的数据行状态,将导致最终DataAdapter.Update()出现不同的行为,例如对于Added状态的行,将导致insert操作、Modified状态将导致update操作、Deleted状态将导致delete操作。

6)使用DataRowState
除了Update()方法内部使用DataRowState外,在我们自己写的代码中,也可以将它与GetChanges()方法配合使用,以获取DataTable的当前变化,参见以下代码,在你获得所有发生更新的行后,实际上你可以自己构造Update SQL命令,而不使用CommandBuilder,当然这需要用到稍后会提到的DataRowVersion。   
Code
  1. private void button4_Click(object sender, EventArgs e)
  2. {
  3.        DataTable dt = new DataTable();
  4.        dt.Columns.Add("fname");
  5.        dt.Columns.Add("fvalue");
  6.        dt.Rows.Add("zhang", 100);
  7.        dt.AcceptChanges();
  8.        dt.Rows[0]["fvalue"] = 101
  9.        //get all Modified rows,then you can use UPDATE SQL to save data.
  10.        DataTable dt1 = dt.GetChanges(DataRowState.Modified);
  11. }
复制代码
Code
  1. private void button3_Click(object sender, EventArgs e)
  2. {
  3.        DataTable dt = new DataTable();
  4.        dt.Columns.Add("fname");
  5.        dt.Columns.Add("fvalue");
  6.        DataRow dr = dt.NewRow();
  7.        DataRowState s = dr.RowState;//detached
  8. }
复制代码

评分

参与人数 3赞扬 +3 收起 理由
DaisyNet + 1 赞一个
ssa521 + 1 赞一个
眯眼笑 + 1 Thanks

查看全部评分

回复

使用道具 举报

0

精华

0

贡献

60

赞扬

帖子
138
软币
2531
在线时间
317 小时
注册时间
2013-6-8
发表于 2013-6-11 00:16:18 | 显示全部楼层
Thnx Bro ,

very good  Details
回复

使用道具 举报

0

精华

0

贡献

15

赞扬

帖子
15
软币
130
在线时间
4 小时
注册时间
2013-6-11
发表于 2013-6-12 06:03:05 | 显示全部楼层
Hmm Good tutorial. Appreciate
回复

使用道具 举报

0

精华

121

贡献

92

赞扬

帖子
81
软币
3644
在线时间
272 小时
注册时间
2013-6-8
发表于 2013-6-18 10:04:03 | 显示全部楼层
学习了,不错的东东。
回复

使用道具 举报

0

精华

78

贡献

55

赞扬

帖子
166
软币
5528
在线时间
819 小时
注册时间
2013-7-11
发表于 2013-7-11 09:51:04 | 显示全部楼层
写的很好,期待下文。
回复

使用道具 举报

0

精华

134

贡献

117

赞扬

帖子
151
软币
1620
在线时间
90 小时
注册时间
2013-6-8
发表于 2013-7-11 17:24:53 | 显示全部楼层
浅显易懂, 顺便温习了一下
回复

使用道具 举报

Archiver|手机版|小黑屋|开发者网 ( 苏ICP备08004430号-2 )
版权所有:南京韵文教育信息咨询有限公司

GMT+8, 2024-5-3 14:13

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表