本文内容来自<<Inside Microsoft Dynamics AX4.0>>,仅供AX爱好者学习交流之用,因原文版权问题,请勿转载,谢谢.
RecordViewCache允许通过X++代码建立成批记录(译注:原文是set-based,意思就是一批记录而不是一条记录的意思,翻译起来还很真别扭)的缓存.通过如下代码初始化缓存:
select nofetch custTrans
where
custTrans
.accountNum
==
'
4000
'
;
recordViewCache
=
new
RecordViewCache(custTrans);
要缓存的记录在select语句中定义,该语句应该包含nofetch这个关键字来阻止从数据库中筛选记录.在将该记录缓存(译注:原文record buffer)做为参数实例化RecordViewCache对象时记录被选择.在RecordViewCache销毁之前,如果能够匹配实例化RecordViewCache对象时指定的where字句,select语句将会在缓存中执行.如下语句显示了如何初始化并使用缓存:
static
void
RecordViewCache(Args _args)
{
CustTrans custTrans;
RecordViewCache recordViewCache;
;
select nofetch custTrans
// Define records to cache.
where custTrans.AccountNum == '4000';
recordViewCache = new RecordViewCache
(custTrans); // Cache the records.
select firstonly custTrans
// Use cache.
where custTrans.AccountNum == '4000' &&
custTrans.CurrencyCode == 'USD';
}
缓存只能在服务器端被实例化,定义的select语句在where字句中只能包含等于(==)断言,并且只能在实例化缓存对象的过程中访问.如果用来实例化缓存对象的表缓存(译注:原文table buffer)是临时表,或者使用了全表缓存,RecordViewCache对象将不会被实例化.
这些记录在缓存中以记录的连接列表的形式存放.因此当在缓存中用一定的查询规则查询时就会顺序搜索缓存.当定义使用缓存的select语句时,可以指定排序顺序,这使得运行时在缓存上创建一个临时索引,它包含在select语句中请求的记录.在返回记录时,应用运行时遍历临时索引,如果没有指定排序,应用运行时会遍历连接列表.
如果在RecordViewCache中缓存的表也使用了记录缓存,应用运行时会采用两者.如果select语句运行在Found缓存表(译注:就是表的缓存属性选了Found)上并且select语句有资格在缓存中查询,应用运行时会首先在缓存中查询,如果没有发现并且该select语句也有资格在RecordViewCache中查询,运行时会使用
RecordViewCache 并在得到记录后更新Found缓存.
满足缓存准则(译注:就是在满足where条件的)的记录在插入,更新和缓存时,在将数据操作语句(DML)传到数据库的同时也会反映到缓存中.缓存中的记录总是插入到连接列表的最后.这种行为会有一定危险,如果应用逻辑在遍历缓存记录的同时插入满足缓存准则的记录时会发生死循环.如下的代码会发生死循环,RecordViewCache对象缓存了CustGroup为40的客户,当执行select语句时,这段代码遍历缓存中的记录,但是因为每一个缓存的记录都被复制了,并且同样用CustGroup '40'插入,记录被插入到缓存的结尾,最终,新插入的记录也被重复获取.
static
void
InfiniteLoop(Args _args)
{
CustTable custTable;
RecordViewCache recordViewCache;
custTable custTableInsert;
;
select nofetch custTable //
Define records to cache.
where custTable.CustGroup == '40';
recordViewCache = new RecordViewCache
(custTable); // Instantiate cache.
ttsbegin;
while select custTable //
Loop over cache.
where custTable.CustGroup == '40'
{
custTableInsert.data(custTable);
custTableInsert.AccountNum =
'dup'+custTable.AccountNum;
custTableInsert.insert(); //
Will insert at end of cache.
//
Records will eventually be selected.
}
ttscommit;
}
为了避免死循环,当从缓存中选择记录时对记录排序,这会创建一个临时索引,它只包含第一次从缓存中读取数据时的记录,任何后来插入的记录都不会被读取.如下面的例子所示,Order by 被应用到select语句中.
static
void
FiniteLoop(Args _args)
{
CustTable custTable;
RecordViewCache recordViewCache;
custTable custTableInsert;
;
select nofetch custTable //
Define records to cache.
where custTable.CustGroup == '40';
recordViewCache = new RecordViewCache
(custTable); // Instantiate cache.
ttsbegin;
while select custTable //
Loop over a sorted cache.
order by CustGroup //
Create temporary index.
where custTable.CustGroup == '40'
{
custTableInsert.data(custTable);
custTableInsert.AccountNum =
'dup'+custTable.AccountNum;
custTableInsert.insert(); // Will
insert at end of cache.
// Records
are not inserted in index.
}
ttscommit;
}
RecordViewCache对象中记录的更改不能回滚.如果存在一个或多个RecordViewCache对象,如果ttsabort操作执行了,如果错误发生了导致数据库回滚,RecordViewCache对象依然包含相同的信息.任何打算通过应用逻辑修改的实例化的RecordViewCache对象不应该比修改它的事务范围的生命周期长.RecordViewCache对象必须声明在一个方法中,该方法在事务开始后才执行,这样在回滚时,对象和缓存同时被销毁.
正如前面讨论的那样,
RecordViewCache 对象是用连接列表实现的,只允许记录的顺序搜索,当缓存大量记录时会造成效率的损失,缓存的使用会比从数据库中获取数据耗费更多的时间,因为数据库可以使用更优化的搜索算法.特别是只需要搜索记录子集的时候.因为在缓存的记录中没有索引,应用运行时必须不断地用更细粒度的where子句匹配缓存记录中的每条记录.
但是,对于小批量的数据,或者同样的记录要被多次遍历的时候,比起从数据库中多次抓取相同的记录,
RecordViewCache 有更好的效率.