之前有几个朋友都提到过GetChanges方法在调用的时候获取不到修改的数据或者获取到的是不对的数据,于是就和朋友一起讨论,也做了DEMO测试,
但是最后还是不了了之。当然结果是朋友换了方法,而自己也没去多在意这个方法,因为自己一直都不用他。
今天又有朋友提到这个问题,于是就花了点时间多了解了一下。当然还是自己写DEMO做测试,最后原因还是没找出来,这多少和项目本身是有点关系的。
问题没解决,但是还是总结了点东西,这里放上DEMO和一些个人见解。本人抛砖,如果引不了玉,也很欢迎各位砸砖。
通常情况下,GetChanges方法是能获取到表自上次调用AcceptChanges以来的所发生的所有更改的,包括增加、修改、删除(删除以空行表示)。
获取不到的情况估计就要根据不同的情况进行调试了,程序一个细微的操作,比如焦点变化、鼠标操作等都有可能引起相关事件的调用执行。
而这些事件都可能影响表GetChanges方法获取数据的正确性,到这里,我都是在猜测……。
下面来讲讲GetChanges方法大概的执行过程。.NET提供给我们的仅仅是GetChanges方法而已,而GetChanges到底是如何运作的,我们不知道,
因为.NET把这些操作都封装了。.NET公开给我们可供我们调用的就是我们在开发环境里可以点出来的,而执行这些可调用方法的另一些内部
(internal等,后面将会介绍)方法或者属性等等,我们只能通过调试的时候查看或者通过反编译器来查看。
首先我们可以通过反编译工具来查看GetChanges方法的执行内容,这里我用的是Reflector,因为他显示出的源代码就是我们的逻辑代码。
在发现自己对某些东西模糊不清的时候,就应该试着去搞清楚,所以后面会整理一些反编译和反汇编的东西,因为突然想起IL代码。
这里就不多说了,自己都不是很清楚的东西怕误导别人,特别是概念这东西。可以肯定的是,虽然Reflector有反汇编的功能,
但这里是反编译源代码……
因为测试的是DataTable的GetChanges方法,所以我们就首先得找到DataTable所属的DLL,
“C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.Data.dll”如果默认安装没有改动的话,
位置应该就是这里了。这里的测试环境是.NET 2005。到这里,觉得自己有点��嗦,但是因为是写给新手区的,
所以就��嗦点吧。Reflector的使用还是很简单的,这里就不做介绍了,我们只要一直点+号找到DataTable然后再
找到GetChanges然后就可以找到我们需要的东西了。最终的结果是:
首先我们注意返回值,是我们需要的DataTable,方法最后返回的是table2,而table2的前身是table,而table在
获取的时候判断了row.oldRecord != row.newRecord
而newRecord和oldRecord 是我们在开发环境里无法调用了,因为他们被封装了。查看Reflector我们看到
internal int oldRecord;
说明他是程序集内部使用的,所以我们无法调用。但是我们可以查看,Reflector里或者在开发调试的时候。
GetChanges会通过判断数据行的Record来获取变更过的数据行。既然Record是对于数据行的,数据行有集合
那么record也会有集合来对应,而那个集合就是
internal sealed class RecordManager
{
// Fields
private readonly List<int> freeRecordList;
private int lastFreeRecord;
private int minimumCapacity;
private int recordCapacity;
private DataRow[] rows;//就是这个,这里会保持RECORD,包括OLD和NEW
private readonly DataTable table;
// Methods
internal RecordManager(DataTable table);
internal void Clear(bool clearAll);
internal int CopyRecord(DataTable src, int record, int copy);
internal void FreeRecord(ref int record);
private void GrowRecordCapacity();
internal int ImportRecord(DataTable src, int record);
internal static int NewCapacity(int capacity);
internal int NewRecordBase();
private int NormalizedMinimumCapacity(int capacity);
internal void SetKeyValues(int record, DataKey key, object[] keyValues);
internal void SetRowCache(DataRow[] newRows);
[Conditional("DEBUG")]
internal void VerifyRecord(int record);
[Conditional("DEBUG")]
internal void VerifyRecord(int record, DataRow row);
// Properties
internal DataRow this[int record] { get; set; }
internal int LastFreeRecord { get; }
internal int MinimumCapacity { get; set; }
internal int RecordCapacity { get; set; }
}
后面会通过调试来说明getChanges判断RECORD的大概过程
运行DEMO的时候,在获取到表的更改的时候,我们可以在更改获取的时候设置一个断点,
进入断点的时候监视方式更改的表。注意是更改的源表,而不是获取到的已经更改的表。如图
因为要查看的是非公开的成员,所以我们这里选最后一个“非公共成员”进行查看。进入非公共成员后选择recordmanager
进入recordManager后我们就发现了ROWS集合,这里集合的大小是以128为单位递增的。如果行集合小于128则大小为128,
如果大于128小于256则大小为256,以此类推。
进入ROWS集合
点开随便一个ROWS,我们就能查看ROWS的ROWSTATE、olerecord和newrecord了。
RowState记录的是的状态,有修改,增加,删除(如果行被删除,这里会显示为NULL,但是查看对象还是能看到rowstate的值)
这里来讲讲最重要的oldrecord和newrecord的真正用处。
拿修改来说:
如果原本的数据是3行
列1 列2
1 11
2 22
3 33
修改第二行
列1 列2
1 11
2 44
3 33
这时候rows集合里的存储是这样的
1 11 newrecord=1,oldrecord=1
2 22 newrecord=2,oldrecord=2
3 33 newrecord=3,oldrecord=3
2 44 newrecord=4,oldrecord=2
这里的第四行就是我们更改的行,大家应该能看出规律吧,如果再修改第三行的话
则会再添加一行,如果把33改为55,则新添加到记录会是
3 55 newrecord=5,oldrecord=3到此,上完色不知道怎么去色了……
上面只是说明getchanges的内部机理,希望不会越描越黑。DEMO 原文地址 http://www.cnblogs.com/ruanbl/archive/2009/06/06/1497380.html