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 " ); // 4 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 ----------------------------------