深入.NET DataTable

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
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)来完成(以参数的形式),例如,   Code
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中是一个比较隐蔽的但又需要十分关注的问题,后续会有相应的说明),参见以下代码。  Code
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循环遍历时出现索引越界异常。  Code
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
}
3 )Exception:Deleted row information cannot be accessed through the row.  Code
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。   Code
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中的数据行,此状态我们不必太关心。参见,   Code
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()等方法实现一个类似于“回滚”的功能。  Code
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(),下面是该方法的摘要,  Code
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];
        }
    }
}在ParameterInput()方法中,调用了GetParameterSourceVersion()方法,  Code
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);
}以行被更新的情况为例,在为参数的赋值的过程中,系统会将相应要更新的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()  Code
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( null null , 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( null null , 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( null null , 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( null null , 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( null null , 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( null null , 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( null null , 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());

结果输出:
-----------------------------------------------
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   Code
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());
}
// ----------------------------------------------- 
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的结构(不包含数据)。
顺便提一下深、浅拷贝的区别,浅拷贝创建原对象类型的一个新实例,复制原对象的所有值类型成员,对于引用类型成员,则只复制该值的指针。深拷贝则复制原对象的所有成员,对于引用类型成员,亦复制其所指的堆上的对象。   Code
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  Code
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
----------------------------------
 

你可能感兴趣的:(Datatable)