作为数据存储和展示的中间对象,FormDataSource在Form中的地位举足轻重,可以说理解了FormDataSource中的方法和事件,也就掌握了Form大部分的内容.本文试图解释清楚FormDataSource的主要方法和属性.
由于Dynamics Axapta的帮助文档还在完善中,本文的部分解释来自于自己的测试,也许不能全面地反映设计者的意图.有纰漏的地方,还望看到本文的同行多多指教.
数据访问
由于Axapta的平台核心代码是没有公开的,FormDataSource方法的代码是看不到也跟踪不到的,所以没办法知道它内部的处理逻辑,不过还是可以用现有的工具窥探其原理,对于数据访问层,我们可以用Axapta提供的SQL跟踪工具,通过查看它执行的SQL语句来推测,如何设置SQL跟踪,这里就不赘述了.
这里以一个简单的示例测试来说明,创建一个窗体CustTableTest,设定其数据源为CustTable,并在Design中创建一个Grid,将CustTable的AccoutNum和Name字段拖到上面.
点击数据源的属性,可以看到如下几个与数据抓取有关的属性:
1.AutoQuery
解释:
FormDataSource这个类中有个方法是Query,原型如下:
public final Query query( [Query _value] )
Axapta给FormDataSource提供了两种访问数据库的方式,一种是通过Query,另一种是通过SQL语句,当然这两种方式都是通过FormDataSource的ExcuteQuery方法去执行的,如果FormDataSource的Query方法返回的对象被实例化了,则会调用QueryRun实现查询,如果没有则会直接采用与数据源相关的语句实现查询(这个没有相关的文档介绍,可以通过跟踪生成的SQL语句来查看.
如果设定AutoQuery的属性为Yes,则窗体CustTableTest运行的SQL语句如下(为了节约篇幅,将客户表里的字段用*表示):
User ID: Admin
Time: 15:06:12 2006-12-29
Version: Microsoft Business Solutions-Axapta 3.0 (Build number 1951.4060)
Database: Microsoft SQL Server
SQL statement: SELECT * FROM CUSTTABLE A WHERE (DATAAREAID=?) ORDER BY A.DATAAREAID,A.ACCOUNTNUM OPTION(FAST 1) [ID=12, Reused=Yes]
Call stack:
\Classes\QueryRun\next
\Classes\FormDataSource\executeQuery
可以看出,Axapta是通过QueryRun这个对象来查询数据的.Time: 15:06:12 2006-12-29
Version: Microsoft Business Solutions-Axapta 3.0 (Build number 1951.4060)
Database: Microsoft SQL Server
SQL statement: SELECT * FROM CUSTTABLE A WHERE (DATAAREAID=?) ORDER BY A.DATAAREAID,A.ACCOUNTNUM OPTION(FAST 1) [ID=12, Reused=Yes]
Call stack:
\Classes\QueryRun\next
\Classes\FormDataSource\executeQuery
如果把AutoQuery设为No,则执行的语句如下:
User ID: Admin
Time: 15:08:58 2006-12-29
Version: Microsoft Business Solutions-Axapta 3.0 (Build number 1951.4060)
Database: Microsoft SQL Server
SQL statement: SELECT * FROM CUSTTABLE A WHERE (DATAAREAID=?) ORDER BY A.DATAAREAID,A.ACCOUNTNUM OPTION(FAST 1) [ID=15, Reused=No]
Call stack:
\Classes\FormDataSource\executeQuery
可以看出,Axapta并没有通过QueryRun对象来实现查询,而直接通过excuteQuery来实现了数据的查询.Time: 15:08:58 2006-12-29
Version: Microsoft Business Solutions-Axapta 3.0 (Build number 1951.4060)
Database: Microsoft SQL Server
SQL statement: SELECT * FROM CUSTTABLE A WHERE (DATAAREAID=?) ORDER BY A.DATAAREAID,A.ACCOUNTNUM OPTION(FAST 1) [ID=15, Reused=No]
Call stack:
\Classes\FormDataSource\executeQuery
当然Axapta的BP里说这个AutoQuery必须设为Yes,不过在了解了其原理之后,大可不必受限与此,当初设计者暴露了这个开关,还是有他的用意的.在实现较为复杂的查询时,用Query对象结构来构造查询是有些不方便的.
这里有一个问题是,采用Query的时候可以很容易地猜测到excuteQuery这个方法用Query对象构造了一个QueryRun出来,然后执行查询,我们如果不通过Query来构造查询,如何直接用select from等关键字来构造查询那?excuteQuery怎么知道应该执行什么SQL语句?看不到excuteQuery的源代码只能猜测啦.
将数据源的AutoQuery属性设为No,在数据源CustTable的init方法中添加如下代码:
public void init()
{
super();
select accountNum,Name from custTable
where custTable.AccountNum == '4000';
}
打开窗体,这时查看执行的SQL语句:{
super();
select accountNum,Name from custTable
where custTable.AccountNum == '4000';
}
User ID: Admin
Time: 15:22:03 2006-12-29
Version: Microsoft Business Solutions-Axapta 3.0 (Build number 1951.4060)
Database: Microsoft SQL Server
SQL statement: SELECT A.ACCOUNTNUM,A.NAME,A.RECID FROM CUSTTABLE A(INDEX(I_077ACCOUNTIDX)) WHERE ((DATAAREAID=?) AND (ACCOUNTNUM=?)) ORDER BY A.DATAAREAID,A.ACCOUNTNUM OPTION(FAST 1) [ID=3, Reused=No]
Call stack:
\Forms\CustTableTest\Data Sources\CustTable\Methods\init - line 4
User ID: Admin
Time: 15:22:03 2006-12-29
Version: Microsoft Business Solutions-Axapta 3.0 (Build number 1951.4060)
Database: Microsoft SQL Server
SQL statement: SELECT A.ACCOUNTNUM,A.NAME,A.RECID FROM CUSTTABLE A(INDEX(I_077ACCOUNTIDX)) WHERE ((DATAAREAID=?) AND (ACCOUNTNUM=?)) ORDER BY A.DATAAREAID,A.ACCOUNTNUM OPTION(FAST 1) [ID=4, Reused=No]
Call stack:
\Classes\FormDataSource\executeQuery
我们可以看到第二段SQL脚本,在excuteQuery中执行的SQL语句实际上就是在init中执行的那段.Time: 15:22:03 2006-12-29
Version: Microsoft Business Solutions-Axapta 3.0 (Build number 1951.4060)
Database: Microsoft SQL Server
SQL statement: SELECT A.ACCOUNTNUM,A.NAME,A.RECID FROM CUSTTABLE A(INDEX(I_077ACCOUNTIDX)) WHERE ((DATAAREAID=?) AND (ACCOUNTNUM=?)) ORDER BY A.DATAAREAID,A.ACCOUNTNUM OPTION(FAST 1) [ID=3, Reused=No]
Call stack:
\Forms\CustTableTest\Data Sources\CustTable\Methods\init - line 4
User ID: Admin
Time: 15:22:03 2006-12-29
Version: Microsoft Business Solutions-Axapta 3.0 (Build number 1951.4060)
Database: Microsoft SQL Server
SQL statement: SELECT A.ACCOUNTNUM,A.NAME,A.RECID FROM CUSTTABLE A(INDEX(I_077ACCOUNTIDX)) WHERE ((DATAAREAID=?) AND (ACCOUNTNUM=?)) ORDER BY A.DATAAREAID,A.ACCOUNTNUM OPTION(FAST 1) [ID=4, Reused=No]
Call stack:
\Classes\FormDataSource\executeQuery
由此我们可以猜测,只要在数据源的init方法中(或者其他什么地方,只要在excuteQuery之前就可以了)定义好一个SQL语句(可以包含其他任意表和任意复杂的查询),当然前提是这个SQL语句中必须包含与当前数据源名称相同的表变量(比如本例中的custTable),在excuteQuery执行的时候就会执行这条SQL语句,至于它是的逻辑,俺想了半天都没想通,按照文档的解释,custTable相当于FormDataSource.Cursor, DataSource引用的物理表的当前记录,为什么这样就可以让excuteQuery执行在init定义的SQL语句,就不晓得了.
这里还有个问题,当然不希望同样的SQL语句执行两次,如何使init方法定义一个在excuteQuery执行的查询而在init方法中并不执行查询动作?这或许就是X++里为什么要出现nofetch这个关键字的原因了,原来看文档的时候,总是想这么个稀奇古怪的关键字做啥子用啊?既然当前不执行查询,那就什么时候要执行查询的时候再select不就OK了?何必曲线救国?在这里nofetch就有用武之地了,把前面在init定义的方法修改如下:
public void init()
{
super();
select nofetch accountNum,Name from custTable
where custTable.AccountNum == '4000';
}
这样,再看执行的SQL脚本时,就只剩下在excuteQuery执行一次了.在init方法中定义要执行的SQL脚本,然后在excuteQuery方法中执行,这或许就是nofetch的本意?{
super();
select nofetch accountNum,Name from custTable
where custTable.AccountNum == '4000';
}
应用场景:
绝大多数情况下应该按照BP的要求,将这个属性设为Yes,但如果要构造的查询很复杂,涉及到很多表,用Query难以构造时,可以考虑将其设为No,然后在init方法中用select等X++关键字构造查询.
2.AutoSearch
解释:
这个属性是用来表示在第一次加载窗体的时候要不要执行FormDataSource的excuteQuery方法,如果设为Yes,则在FormRun的Run方法中会调用FormDataSource的excuteQuery方法,否则不调用,由于FormDataSource的数据通过excuteQuery方法取得,不调用该方法意味着窗体不会加载任何数据.
应用场景:
大多数情况下应该按照BP的要求,将该属性设为Yes,记得原来写Asp.Net程序的时候,为了加快页面的加载数据,第一次加载页面的时候并不加载数据,在用户选择条件,点击按钮后再加载,这样加载的数据相对少一些,速度也会快一些,如果有这样的需求的话,可以考虑将该属性设为No.
3.OnlyFetchActive
解释:
主要是理解Acitve,什么的是Active的?就是要在Form上显示的字段.这个属性的意思是,是否只抓取要在Form上显示的数据,不过这个好处只有在AutoQuery设为Yes的时候才会享受到,自己构造Query或者通过SQL查询出来,是不能享受这个待遇的,因为Axapta咋知道你在瞎折腾啥?
另外把这个属性设为Yes,为了保持数据的一致性,Axapt是不允许删除其中的记录的,如果强行删除它会给你个error see see.
应用场景:
这个属性是为Lookup窗体量身打造的,由于只需要从数据库中查询要显示的字段,速度无疑会增加不少.如果是普通窗体建议在没有特别的理由,就不要动这个属性,让它保持为默认值No吧.