之前有几个朋友都提到过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然后就可以找到我们需要的东西了。最终的结果是:

 

public DataTable GetChanges()
{
    DataTable table2;
    IntPtr ptr;
    Bid.ScopeEnter(
out ptr, "<ds.DataTable.GetChanges|API> %d#\n"this.ObjectID);
    
try
    {
        DataTable table 
= this.Clone();
        DataRow row 
= null;
        
for (int i = 0; i < this.Rows.Count; i++)
        {
            row 
= this.Rows[i];
            
if (row.oldRecord != row.newRecord)
            {
                table.ImportRow(row);
            }
        }
        
if (table.Rows.Count == 0)
        {
            
return null;
        }
        table2 
= table;
    }
    
finally
    {
        Bid.ScopeLeave(
ref ptr);
    }
    
return table2;
}

首先我们注意返回值,是我们需要的DataTable,方法最后返回的是table2,而table2的前身是table,而table在

获取的时候判断了. != .

而和 是我们在开发环境里无法调用了,因为他们被封装了。查看Reflector我们看到

;
说明他是程序集内部使用的,所以我们无法调用。但是我们可以查看,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] { getset; }
    
internal int LastFreeRecord { get; }
    
internal int MinimumCapacity { getset; }
    
internal int RecordCapacity { getset; }
}
后面会通过调试来说明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的内部机理,希望不会越描越黑。