深入.NET DataTable(转载)

1、ADO.NET相关对象一句话介绍
1)DataAdapter
DataAdapter实际是一个SQL语句集合,因为对Database的操作最终需要归结到SQL语句。
2)Dataset
DataSet可以理解成若干DataTable的集合,DataSet在内存里面维护一个表集合包括表间关系。对于.NET Framework 2.0之前的版本,DataSetADO.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的目的,例如, 


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

2)
使用 DataAdapter.Update()
另外一种方式,是使用 DataAdapter.Update() 方法,这并不是说我们不需要 SQL 语句了,只是 SQL 语句拼接的工作已经交给了 DataAdapter (实际上是交给了 CommandBuilder )来完成(以参数的形式),例如,  

string c = "select fname,fvalue from table1";
SqlCommand cmd 
= new SqlCommand(c,conn);
SqlDataAdapter da 
= new SqlDataAdapter(cmd);
SqlCommandBuilder scb 
= new SqlCommandBuilder(da); //(1)
DataTable dt = new DataTable();
da.Fill(dt);
dt.Rows[
0].Delete();//(2)
da.Update(dt);
在这里,你看不到 SQL 语句,因为在你初始化 SqlCommandBuilder 的过程中,将自动根据表结构(基于你的 Select 语句 )构造 insert,update,delete 语句。对于上面的代码,你可以获得 SQL 语句内容,
DELETE FROM [table1] WHERE (([fname] = @p1) AND ((@p2 = 1 AND [fvalue] IS NULL) OR ([fvalue] = @p3)))
而执行时候,会传入相应的参数值,
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

xec 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 语句,
UPDATE [table1] SET [fname] = @p1, [fvalue] = @p2 WHERE (([fname] = @p3) AND ((@p4 = 1 AND [fvalue] IS NULL) OR ([fvalue] = @p5)))
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 中是一个比较隐蔽的但又需要十分关注的问题,后续会有相应的说明),参见以下代码。 

private void button1_Click(object sender, EventArgs e)
{
       
try
       {
              dataAdapter1.Fill(dt);
              DataRowState s 
= dt.Rows[0].RowState;//unchanged
       }
       
catch
       {
       }
}
private void button2_Click(object sender, EventArgs e)
{
       DataTable dt 
= new DataTable();
       dt.Columns.Add(
"fname");
       dt.Columns.Add(
"fvalue");
       dt.Rows.Add(
"zhang"100);
       DataRowState s 
= dt.Rows[0].RowState;//added
}

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


3)Exception:Deleted row information cannot be accessed through the row. 

private void button8_Click(object sender, EventArgs e)
{
       DataTable dt 
= new DataTable();
       dt.Columns.Add(
"fname");
       dt.Columns.Add(
"fvalue");
       dt.Rows.Add(
"zhang"100);
       
//
       dt.AcceptChanges();
       dt.Rows[
0].Delete();
       DataRow dr 
= dt.Rows[0]; //No error
       object o = dt.Rows[0]["fvalue"];//Exception,row can be accessed,but row data cannot
}

 


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   

private void button4_Click(object sender, EventArgs e)
{
       DataTable dt 
= new DataTable();
       dt.Columns.Add(
"fname");
       dt.Columns.Add(
"fvalue");
       dt.Rows.Add(
"zhang"100);
       dt.AcceptChanges();
       dt.Rows[
0]["fvalue"= 101
       
//get all Modified rows,then you can use UPDATE SQL to save data.
       DataTable dt1 = dt.GetChanges(DataRowState.Modified);
}


7)状态Detached
除了上图中给出的几种行状态外,还有一种特殊的状态 Detached ,这种状态表示已初始化但未添加到 DataTable 中的数据行,此状态我们不必太关心。参见,    


private void button3_Click(object sender, EventArgs e)
{
       DataTable dt 
= new DataTable();
       dt.Columns.Add(
"fname");
       dt.Columns.Add(
"fvalue");
       DataRow dr 
= dt.NewRow();
       DataRowState s 
= dr.RowState;//detached
}
 
4、行状态、行版本、行数据版本
行版本( DataRowVersion )描述数据行的版本;
行数据版本( DataViewRowState )描述数据行中数据的版本。
这两个概念令人困惑,我认为可以仅仅从用法上对它们进行了解,毕竟我们使用它们的机会并非很大。  

1)
使用 DataRowVersion
关于 DataRowVersion ,以状态为 Modified 的行为例,它包含两个 DataRowVersion (即存储了两行数据): Current,Original ,分别存储该行修改后与修改前的数据,也就是说,行版本实际可以帮助 RejectChanges() 等方法 实现一个类似于“回滚”的功能。  

private void button4_Click(object sender, EventArgs e)
{
       DataTable dt 
= new DataTable();
       dt.Columns.Add(
"fname");
       dt.Columns.Add(
"fvalue");
       dt.Rows.Add(
"zhang"100);
       dt.AcceptChanges();
       dt.Rows[
0]["fvalue"= 101;
       
int i = Convert.ToInt32(dt.Rows[0]["fvalue", DataRowVersion.Original]);//100
       int i2 = Convert.ToInt32(dt.Rows[0]["fvalue", DataRowVersion.Current]);//101
}
同理你可以借助
DataRowVersion 来访问 Deleted 的数据,前面我们提到了对于 Deleted 的数据, 使用 dt.Rows[0]["fvalue"] 访问 将引发异常,可以使用
dt.Rows[0]["fvalue", DataRowVersion.Original]

2) DataRowVersion
Update()
现在我们回想一下,当我们使用 CommandBuilder 构造完 Update,Insert,Delete 命令之后,那些 SQL 命令中的参数怎么办?我们知道在 SQL 命令执行之前,我们必须为所有输入参数指定参数值,那么 Update() 方法内部是如何工作的?这就有赖于 DataRowVersion 了。
 
我们可以简单看一下 Update() 方法使用过程中涉及的相关 .NET 源码,
System.Data.Common.DbDataAdapter
protected virtual int Update(DataRow[] dataRows, DataTableMapping tableMapping);
Update() 方法中,调用了 ParameterInput() ,下面是该方法的摘要   ParameterInput() 方法中,调用了 GetParameterSourceVersion() 方法   以行被更新的情况为例,在为参数的赋值的过程中,系统会将相应要更新的 DataRow 一并传入,同时对于 Update 语句,
UPDATE [table1] SET [fname] = @p1, [fvalue] = @p2 WHERE (([fname] = @p3) AND ((@p4 = 1 AND [fvalue] IS NULL) OR ([fvalue] = @p5)))
我们要了解的一点是, 5 个参数中 @p1,@p2 是一类, @p3, @p5 是一类,它们的区别在于,前一类的 SourceVersion Current ,而后一类的 SourceVersion Original ,这在上述的 GetParameterSourceVersion() 方法中被用到,所以!!,针对传入的需要更新的 DataRow Update() 方法内部将使用当前值(即修改后的值)填充 @p1,@p2 ,而使用原始值(即修改前的值)填充 @p3, @p5 Insert,delete 同理。

3)理解DataRowVersion.Default
对于 Added Modified Deleted 状态的行,其 Default 版本实际是 Current 版本,对于 Unchanged 则无甚区别。
 
4) 使用 DataViewRowState
(1) 配合 DataTable.Select()  
结果输出:
-----------------------------------------------
Added:
li: 100
-----------------------------------------------
CurrentRows:
zhao: 100
qian: 101
li: 100
-----------------------------------------------
Deleted:
sun: 100
-----------------------------------------------
ModifiedCurrent:
qian: 101
-----------------------------------------------
ModifiedOriginal:
qian: 101
-----------------------------------------------
OriginalRows:
zhao: 100
qian: 101
sun: 100
-----------------------------------------------
Unchanged:
zhao: 100
 
(2) 配合 DataView.RowFilter    -----------------------------------------------  
Added & ModifiedCurrent:
qian: 101
li: 100
-----------------------------------------------
 
5)DataViewRowState 中的“复合版本”
DataViewRowState 包含多个枚举成员,我可以给出每个枚举成员对应的 int 值,
Added                     4
CurrentRow              22
Deleted                   8
ModifiedCurrent        16
ModifiedOriginal        32
None                       0
OriginalRow              42
Unchanged              2
你可以发现,其中的两个状态 CurrentRow OriginalRow 实际是经由其它几种状态二进制或运算的结果,
CurrentRow=Added|ModifiedCurrent|Unchanged
OriginalRow=Deleted|ModifiedOriginal|Unchanged
 
5、了解其它几个方法
1)Delete() Remove() Clear()
DataRow.Delete()
DataRowCollection.Remove()
DataTable.Clear() DataSet.Clear()
正如前面所述,对于 DataRow Delete() 方法,其内部的处理并未真正删除此行,而只是将行标识为 Deleted ,并“移除”了它的 Current 版本。这样,当使用 DataAdapter Update() 进行更新时,其内部机制可以根据仍然存在的 Original 版本数据,为 DeleteCommand 填充参数,完成更新数据库的操作。
Clear() 方法则完全删除了堆上的数据行对象,并且将对数据引用置空(这点可以参见 Clear() 方法的反编译代码),这种情况下无法生成可执行的 DeleteCommand ,这就是说,当你用 Clear() 方法“清空” DataTable 后,使用 Update() 方法并不能像你预想的一样将对应的数据库表数据删除。
另外,需要注意一点是 Delete() 并不导致数据行减少(除非原行是 Added 状态),当然,如果是对 Added 状态的行执行 Delete() ,则导致行数减少。当你使用 for 循环时,这可能会造成问题。
另外,我们还有一个方法: DataRowCollection.Remove() ,其作用类似于 Clear() ,是彻底地移除行,假设你是使用 DataAdapter.Update() 方法更新 Database ,那么你将没有机会将你的删除操作同步到 Database 中。

2)Copy() Clone()
.NET 中有两类拷贝,浅拷贝 (Shadow copy) 、深拷贝 (Deep copy) ,对于大多数我们所见的类(比如常见的集合类等等),没有深拷贝方法,大多数会有一个浅拷贝方法 Clone() 我唯一所见的一个深拷贝方法是 DataTable.Copy() ,同时, DataTable.Clone() 方法也比较特殊,因为它并非是浅拷贝方法,而是拷贝 DataTable 的结构(不包含数据)。
顺便提一下深、浅拷贝的区别,浅拷贝创建原对象类型的一个新实例,复制原对象的所有值类型成员,对于引用类型成员,则只复制该值的指针。深拷贝则复制原对象的所有成员,对于引用类型成员,亦复制其所指的堆上的对象。   
同理你可以借助

System.Data.Common.DbDataAdapter 
private void ParameterInput(IDataParameterCollection parameters, StatementType typeIndex, DataRow row, DataTableMapping mappings)
{
    
foreach (IDataParameter parameter in parameters)
    {
        
if (column != null)
        {
            DataRowVersion parameterSourceVersion 
= GetParameterSourceVersion(typeIndex, parameter);
            parameter.Value 
= row[column, parameterSourceVersion];
        }
    }
}

System.Data.Common.DbDataAdapter 
private static DataRowVersion GetParameterSourceVersion(StatementType statementType, IDataParameter parameter)
{
    
switch (statementType)
    {
        
case StatementType.Select:
        
case StatementType.Batch:
            
throw ADP.UnwantedStatementType(statementType);
        
case StatementType.Insert:
            
return DataRowVersion.Current;
        
case StatementType.Update:
            
return parameter.SourceVersion;
        
case StatementType.Delete:
            
return DataRowVersion.Original;
    }
    
throw ADP.InvalidStatementType(statementType);
}

private void button9_Click(object sender, EventArgs e)
{
       DataTable dt 
= new DataTable();
       dt.Columns.Add(
"fname");
       dt.Columns.Add(
"fvalue");
       dt.Rows.Add(
"zhao"100);
       dt.Rows.Add(
"qian"100);
       dt.Rows.Add(
"sun"100);
       dt.AcceptChanges();
       
//
       dt.Rows[1]["fvalue"= 101;
       dt.Rows[
2].Delete();
       dt.Rows.Add(
"li"100);
       
//object o = dt.Rows[2]["fvalue", DataRowVersion.Original];
       
//
       StringBuilder sb = new StringBuilder();
       DataRow[] drs 
= dt.Select(nullnull, DataViewRowState.Added);
       sb.AppendLine(
"-----------------------------------------------");
       sb.AppendLine(
"Added:");
       
for (int i = 0; i < drs.Length; i++)
       {
              sb.AppendLine(drs[i][
"fname"].ToString() +""+ drs[i]["fvalue"].ToString());
       }
       drs 
= dt.Select(nullnull, DataViewRowState.CurrentRows);
       sb.AppendLine(
"-----------------------------------------------");
       sb.AppendLine(
"CurrentRows:");
       
for (int i = 0; i < drs.Length; i++)
       {
              sb.AppendLine(drs[i][
"fname"].ToString() + "" + drs[i]["fvalue"].ToString());
       }
       drs 
= dt.Select(nullnull, DataViewRowState.Deleted);
       sb.AppendLine(
"-----------------------------------------------");
       sb.AppendLine(
"Deleted:");
       
for (int i = 0; i < drs.Length; i++)
       {
              sb.AppendLine(drs[i][
"fname", DataRowVersion.Original].ToString() + "" + drs[i]["fvalue", DataRowVersion.Original].ToString());
       }
       drs 
= dt.Select(nullnull, DataViewRowState.ModifiedCurrent);
       sb.AppendLine(
"-----------------------------------------------");
       sb.AppendLine(
"ModifiedCurrent:");
       
for (int i = 0; i < drs.Length; i++)
       {
              sb.AppendLine(drs[i][
"fname"].ToString() + "" + drs[i]["fvalue"].ToString());
       }
       drs 
= dt.Select(nullnull, DataViewRowState.ModifiedOriginal);
       sb.AppendLine(
"-----------------------------------------------");
       sb.AppendLine(
"ModifiedOriginal:");
       
for (int i = 0; i < drs.Length; i++)
       {
              sb.AppendLine(drs[i][
"fname"].ToString() + "" + drs[i]["fvalue"].ToString());
       }
       drs 
= dt.Select(nullnull, DataViewRowState.OriginalRows);
       sb.AppendLine(
"-----------------------------------------------");
       sb.AppendLine(
"OriginalRows:");
       
for (int i = 0; i < drs.Length; i++)
       {
              
if (drs[i].RowState == DataRowState.Deleted)
              {
                     sb.AppendLine(drs[i][
"fname", DataRowVersion.Original].ToString() + "" + drs[i]["fvalue", DataRowVersion.Original].ToString());
              }
              
else
              {
                     sb.AppendLine(drs[i][
"fname"].ToString() + "" + drs[i]["fvalue"].ToString());
              }
       }
       drs 
= dt.Select(nullnull, DataViewRowState.Unchanged);
       sb.AppendLine(
"-----------------------------------------------");
       sb.AppendLine(
"Unchanged:");
       
for (int i = 0; i < drs.Length; i++)
       {
              sb.AppendLine(drs[i][
"fname"].ToString() + "" + drs[i]["fvalue"].ToString());
       }
       MessageBox.Show(sb.ToString());
}
 

private void button10_Click(object sender, EventArgs e)
{
       DataTable dt 
= new DataTable();
       dt.Columns.Add(
"fname");
       dt.Columns.Add(
"fvalue");
       dt.Rows.Add(
"zhao"100);
       dt.Rows.Add(
"qian"100);
       dt.Rows.Add(
"sun"100);
       dt.AcceptChanges();
       
//
       dt.Rows[1]["fvalue"= 101;
       dt.Rows[
2].Delete();
       dt.Rows.Add(
"li"100);
       
//
       DataView dv = new DataView(dt);
       dv.RowStateFilter 
= DataViewRowState.Added | DataViewRowState.ModifiedCurrent;
       StringBuilder sb 
= new StringBuilder();
       sb.AppendLine(
"-----------------------------------------------");
       sb.AppendLine(
"Added & ModifiedCurrent:");
       
for (int i = 0; i < dv.Count; i++)
       {
              sb.AppendLine(dv[i][
"fname"].ToString() + "" + dv[i]["fvalue"].ToString());
       }
       sb.AppendLine(
"-----------------------------------------------");
       MessageBox.Show(sb.ToString());
}
//

static void Main(string[] args)
{
       DataTable dt 
= new DataTable();
       dt.Columns.Add(
"fname"typeof(System.String));
       dt.Columns.Add(
"fvalue"typeof(System.Int32));
       
for (int i = 1; i <= 10; i++)

       {
              dt.Rows.Add(
"p" + i, i);
       }
       dt.AcceptChanges();
       
//
       DataTable dtc = dt.Copy();
       
//
       bool b = object.ReferenceEquals(dt.Rows[0], dtc.Rows[0]);//false
       DataRowState s = dtc.Rows[0].RowState;//Unchanged
       
//Clone() and ImportRow()
       DataTable dtc2 = dt.Clone();
       
for (int i = 0; i < 5; i++)
       {
              dtc2.ImportRow(dt.Rows[i]);
       }
       
bool b2 = object.ReferenceEquals(dt.Rows[0], dtc2.Rows[0]);//false
       DataRowState s2 = dtc2.Rows[0].RowState;//Unchanged
       
//ItemArray
       DataTable dtc3 = dt.Clone();
       
for (int i = 0; i < 5; i++)
       {
              dtc3.Rows.Add(dt.Rows[i].ItemArray);
              
//dtc3.Rows.Add(dt.Rows[i]);//run time exception
       }
       
bool b5 = object.ReferenceEquals(dt.Rows[0], dtc3.Rows[0]);//false
       DataRowState s5 = dtc3.Rows[0].RowState;//Added
       
//
       ArrayList al = new ArrayList();
       al.Add(
"xy");
       ArrayList alc 
= al.Clone() as ArrayList;
       
if (alc != null)
       {
              
bool b3 = object.ReferenceEquals(al, alc);//false
              bool b4 = object.ReferenceEquals(al[0], alc[0]);//true
       }
}
 
3)Select() Compute()
这两个方法在很多情况下都有助于你简化代码,避免每一次使用循环遍历 DataTable ,参见以下,
对于这两个方法中可用的表达式,参见,
http://msdn.microsoft.com/en-us/library/system.data.datacolumn.expression.aspx 

class Program
{
       
static void Main(string[] args)
       {
              DataTable dt 
= new DataTable();
              dt.Columns.Add(
"fname",typeof(System.String));
              dt.Columns.Add(
"fvalue",typeof(System.Int32));
              
for (int i = 1; i <= 10; i++)
              {
                     dt.Rows.Add(
"p" + i, i);
              }
              dt.AcceptChanges();
              
//
              DataRow[] drs = dt.Select("fvalue>6");
              PrindRows(drs);
              
//
              drs = dt.Select("fvalue>6 and fvalue<9");//AND OR NOT
              PrindRows(drs);
              
//
              drs = dt.Select("fname like 'p1%'");
              PrindRows(drs);
              
//
              drs = dt.Select("fname in ('p1','p3')");//< > <= >= <> = IN LIKE
              PrindRows(drs);
              
//
              drs = dt.Select("fvalue=max(fvalue)");//SUM AVG MIN MAX COUNT STDEV VAR
              PrindRows(drs);
              
//
              drs = dt.Select("fvalue%2=0");//+ - * / %
              PrindRows(drs);
              
//
              drs = dt.Select("len(fname)=3");//LEN(expression) ISNULL(expression, replacementvalue) IIF(expr, truepart, falsepart) TRIM(expression) SUBSTRING(expression, start, length)
              PrindRows(drs);

              
object o = dt.Compute("count(fname)""fvalue>6");//
              Console.WriteLine(o.ToString());
              Console.Read();
       }

       
static void PrindRows(DataRow[] pDrs)
       {
              Console.WriteLine(
"----------------------------------");
              
foreach (DataRow dr in pDrs)
              {
                     Console.WriteLine(dr[
"fname"].ToString().PadRight(8' '+ dr["fvalue"].ToString());
              }
              Console.WriteLine(
"----------------------------------");
       }
}

 


----------------------------------
p7      7
p8      8
p9      9
p10     10
----------------------------------
 
----------------------------------
p7      7
p8      8
----------------------------------
 
----------------------------------
p1      1
p10     10
----------------------------------
 
----------------------------------
p1      1
p3      3
----------------------------------
 
----------------------------------
p10     10
----------------------------------
 
----------------------------------
p2      2
p4      4
p6      6
p8      8
p10     10
----------------------------------
 
----------------------------------
p10     10
----------------------------------

private void button7_Click(object sender, EventArgs e)
{
       DataTable dt 
= new DataTable();
       dt.Columns.Add(
"fname");
       dt.Columns.Add(
"fvalue");
       dt.Rows.Add(
"zhang"100);
       
//
       dt.Rows[0].Delete();
       
int c = dt.Rows.Count;//0
}
private void button8_Click(object sender, EventArgs e)
{
       DataTable dt 
= new DataTable();
       dt.Columns.Add(
"fname");
       dt.Columns.Add(
"fvalue");
       dt.Rows.Add(
"zhang"100);
       
//
       dt.AcceptChanges();
       dt.Rows[
0].Delete();
       
int c = dt.Rows.Count;//1
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值