VFP的数据战略:高等篇
关于Java/Kotlin下载图片,图片打开不能显示问题探究
引语
在“”一文中,我们研讨了VFP应用程序中接见非VFP数据(如SQL Server)的差别机制:长途视图、SQL Passthrough、ADO、XML和VFP 8中增加的CursorAdapter类。在本文中,我们将更细致地议论CursorAdapter,并议论可重用数据类的观点。另外,我们将扼要引见新的XMLAdapter基类,并相识它怎样协助与其他源(如ADO.NET)交流数据。
CursorAdapter
在我看来,CursorAdapter是VFP 8中最大的新特征之一。我认为他们这么酷的原因是:
- 使得运用ODBC、ADO或XML变得轻易,纵然您不太熟悉这些手艺。
- 为长途数据供应了一致的接口,而不论您挑选何种机制。
- 使从一种机制切换到另一种机制变得轻易。
末了是一个例子。假定您有一个应用程序运用带有CursorAdapter的ODBC来接见SQL Server数据,出于某种原因,您愿望变动成运用ADO相反。您只需变动CursorAdapters的DataSourceType并变动到后端数据库的衔接,就完成了。应用程序中的其他组件既不晓得也不关心这一点;它们依然看到同一个游标,而不论用于接见数据的机制怎样。
让我们入手下手经由历程检察CursorAdapter的属性、事宜和要领(PEMs)来搜检它们。
PEMS
这里我们不议论CursorAdapter类的一切属性、事宜和要领,只议论更主要些的属性、事宜和要领。有关完全列表,请参阅VFP文档。
(PEMS:属性、事宜、要领统称的缩写——译者注)
DataSourceType
这个属性很主要:它决议了类的行动,以及将什么范例的值放入其他一些属性中。有效的选项是“Native”,这示意您运用的是Native表,或许是挑选“ODBC”、“ADO”或“XML”,这示意您运用了恰当的机制来接见数据。您大概不会运用“Native”,因为您大概会运用Cursor对象而不是CursorAdapter,但此设置将使今后升迁应用程序更轻易。
DataSource
这是接见数据的要领。当DataSourceType设置为“Native”或“XML”时,VFP疏忽此属性。关于ODBC,将DataSource设置为有效的ODBC衔接句柄(这意味着您必须本身治理衔接)。关于ADO,数据源必须是一个ADO纪录集,该纪录集的ActiveConnection对象设置为翻开的ADO衔接对象(一样,您必须本身治理)。
UseDEDataSource
假如此属性设置为.T(默认值为.F),则可以不运用DataSourceType和DataSource属性,因为CursorAdapter将运用数据环境(DataEnvironment)的属性(VFP 8也将DataSourceType和DataSource增加到DataEnvironment类)。将此设置为.T.的一个示例是,愿望数据环境中的一切CursorAdapter运用雷同的ODBC衔接。
SelectCmd
关于除了XML以外的一切内容,这是用于检索数据的SQL SELECT敕令。关于XML,这可以是可以转换为游标的有效XML字符串(运用内部XMLTOCURSOR()挪用)或返回有效XML字符串的表达式(如UDF)。
CursorSchema
此属性保留游标的组织,其花样与您在CREATE Cursor敕令中运用的花样雷同(此类敕令中括号之间的一切内容)。这里有一个例子:CUST_ID C(6),COMPANY C(30),CONTACT C(30),CITY C(25)。只管可以将此项留空,并通知CursorAdapter在竖立游标时肯定组织,但假如将CursorSchema添补进来,效果会更好。起首,假如CursorSchema为空或不正确,则在翻开窗体的数据环境时大概会失足,或许没法将字段从CursorAdapter拖放到窗体以竖立控件。荣幸的是,VFP附带的CursorAdapter构建器可以自动为您添补这个内容。
AllowDelete, AllowInsert, AllowUpdate, and SendUpdates
这些属性(默认为.T)决议是不是可以实行删除、插进去和更新,以及是不是将变动发送到数据源。
KeyFieldList, Tables, UpdatableFieldList, and UpdateNameList
假如愿望VFP运用游标中所做的变动自动更新数据源,则须要这些属性,这些属性的用处与视图的同名CursorSetProp()属性雷同。KeyFieldList是一个逗号分开的字段列表(不带别号),这些字段组成游标的主键。表是一个逗号分开的表列表。UpdateableFieldList是一个逗号分开的字段列表(没有别号),可以更新。UpdateNameList是一个逗号分开的列表,它将游标中的字段名与表中的字段名相婚配。UpdateNameList的花样以下:CursorFieldName1 Table.FieldName1,CursorFieldName2 Table.FieldName2……请注重,纵然UpdateableFieldList不包含表的主键的称号(因为您不愿望更新该字段),它也必须依然存在于UpdateNameList中,不然更新将不起作用。
*Cmd, *CmdDataSource, *CmdDataSourceType
假如要迥殊掌握VFP怎样删除、插进去或更新数据源中的纪录,可以为这些属性集指定恰当的值(将上面的*替换为Delete、Insert和Update)。
CursorFill(UseCursorSchema, NoData, Options, Source)
此要领竖立游标并用数据源中的数据添补它(只管可以经由历程.T.使NoData参数竖立空游标)。关于第一个运用CursorSchema或.F中定义的情势的参数,通报.T。以从数据源竖立恰当的组织(在我看来,这类行动是相反的)。必须设置多锁,不然此要领将失利。假如CursorFill因为任何原因失利,它将返回.F,而不是引发毛病;运用AERROR()来肯定出了什么问题(只管准备好举行一些深挖,因为您常常收到的毛病音讯不够详细,没法确实地通知您问题是什么)。
CursorRefresh()
此要领类似于ReQuery()函数:它革新游标的内容。
Before*() and After*()
险些每一个要领和事宜都有前后“钩子”事宜,许可您自定义CursorAdapter的行动。比方,在AfterCursorFill中,可以为游标竖立索引,使其一直可用。关于Before事宜,可以返回.F.以防备触发它的操纵发作(这与数据库事宜类似)。
下面是一个示例(CursorAdapterExample.prg),它从SQL Server附带的Northwind数据库的Customers表中猎取巴西客户的某些字段。游标是可更新的,因而假如您在游标中举行了变动,请将其封闭,然后再次运转程序,您将看到您的变动已保留到后端。
local loCursor as CursorAdapter, ; laErrors[1] loCursor = createobject('CursorAdapter') with loCursor .Alias = 'Customers' .DataSourceType = 'ODBC' .DataSource = sqlstringconnect('driver=SQL Server;' + ; 'server=(local);database=Northwind;uid=sa;pwd=;trusted_connection=no') .SelectCmd = "select CUSTOMERID, COMPANYNAME, CONTACTNAME " + ; "from CUSTOMERS where COUNTRY = 'Brazil'" .KeyFieldList = 'CUSTOMERID' .Tables = 'CUSTOMERS' .UpdatableFieldList = 'CUSTOMERID, COMPANYNAME, CONTACTNAME' .UpdateNameList = 'CUSTOMERID CUSTOMERS.CUSTOMERID, ' + ; 'COMPANYNAME CUSTOMERS.COMPANYNAME, CONTACTNAME CUSTOMERS.CONTACTNAME' if .CursorFill() browse else aerror(laErrors) messagebox(laErrors[2]) endif .CursorFill() endwith
数据环境和表单变动
为了支撑新的CursorAdapter类,对DataEnvironment、Form类及其设想器举行了一些变动。
起首,如前所述,DataEnvironment类如今有DataSource和DataSourceType属性。它本身不运用这些属性,但已将UseDataSource设置为.T.的任何CursorAdapter成员都运用这些属性。其次,如今可以运用类设想器(woo-hoo!)可视化地竖立DataEnvironment子类。
至于表单,如今可以经由历程设置新的DEClass和DEClassLibrary属性来指定要运用的DataEnvironment子类。假如您如许做,您对现有数据环境所做的任何事变(游标、代码等)都将丧失,但至少您会起首收到正告。表单的一个很酷的新特征是BindControls属性;在属性窗口中将其设置为.F. 意味着VFP不会在初始化时尝试对控件举行数据绑定,只需在将BindControls设置为.T.时才会如许做。这有什么优点?好吧,您咒骂参数通报给Init多少次了,Init在一切控件初始化并绑定到它们的ControlSource以后触发?假如要将参数通报给通知它要翻开哪一个表的窗体或其他影响ControlSources的内容,该怎么办?这个新属性使这个问题很快处置惩罚。
其他变化
CursorGetProp('SourceType')返回一个新的值局限:假如游标是用CursorFill竖立的,则该值为100加上旧值(比方,长途数据为102)。假如游标是用CursorAttach竖立的(许可您将现有游标附加到CursorAdapter对象),则该值为200加上旧值。假如数据源是ADO纪录集,则值为104(CursorFill)或204(CursorAttach)。
生成器
VFP包含DataEnvironment和CursorAdapter组织器(或称为生成器——译者注),使得运用这些类越发轻易。
以一般体式格局启动DataEnvironment Builder:在类设想器中右键单击窗体的DataEnvironment或DataEnvironment子类,然后挑选Builder。数据环境生成器的“数据源”页是设置数据源信息的位置。挑选所需的数据源范例和数据源的泉源。假如挑选“运用现有衔接句柄”(ODBC)或“运用现有ADO纪录集”(ADO),请指定包含数据源的表达式(比方“goConnectionMgr.nHandle”)。您还可以挑选运用体系上的任一个DSN或衔接字符串。只需在为ADO挑选“运用衔接字符串”时才会启用“生成”按钮,该按钮将显现“数据链接属性”对话框,您可以运用该对话框直观地生成衔接字符串。假如挑选“运用DSN”或“运用衔接字符串”,生成器将在数据环境的BeforeOpenTables要领中生成代码以竖立所需的衔接。假如挑选“Native”,则可以挑选VFP数据库容器作为数据源;在这类状况下,生成的代码将确保数据库是翻开的(也可以运用自在表作为数据源)。
“Cursors”页面许可您保护DataEnvironment的CursorAdapter成员(游标对象不会在生成器中显现,也不能增加它们)。Add按钮许可您向DataEnvironment增加CursorAdapter子类,而New则竖立一个新的基类CursorAdapter。Remove删除Select CursorAdapter,Builder为所选CursorAdapter挪用CursorAdapter Builder。您可以变动CursorAdapter对象的称号,但关于任何其他属性,都须要CursorAdapter生成器。
从快速菜单中挑选Builder也可以挪用CursorAdapter生成器。“Properties”页显现对象的类和称号(只需在从DataEnvironment中调出生成器时才变动称号,因为它对CursorAdapter子类是只读的)、它将竖立的游标的别号、是不是应当运用DataEnvironment的数据源以及衔接信息(假如没有)。与DataEnvironment生成器一样,假如挑选“运用DSN”或“运用衔接字符串”,CursorAdapter生成器将生成代码以竖立所需的衔接(在本例中是CursorFill要领)。
“数据接见”页许可您指定SelectCmd、CursorSchema和其他属性。假如您指定了衔接信息,可以单击SelectCmd的Build按钮来显现Select Command Builder,如许就可以轻松地竖立SelectCmd。
Select敕令生成器简化了构建一个简朴的Select语句的事变。从“表格”下拉列表中挑选所需的表格,然后将响应的字段移到选定的一侧。关于本机数据源,可以向“表”组合框中增加表(比方,假如愿望运用余暇表)。挑选OK时,SelectCmd将添补恰当的SQL SELECT语句。
单击游标情势的“生成”按钮,自动为您填写此属性。为了使其事变,生成器实际上竖立了一个新的CursorAdapter对象,恰当地设置了属性,并挪用CursorFill来竖立游标。假如您没有到数据源的及时衔接,或许CursorFill因为某种原因(比方无效的SelectCmd)失利,那末这明显行不通。
运用“自动更新”页设置VFP自动为数据源生成更新语句所需的属性。Tables属性是从SelectCmd中指定的表自动添补的,fields网格是从CursorSchema中的字段添补的。与视图设想器一样,可以经由历程搜检网格中的响应列来挑选哪些是症结字段,哪些字段是可更新的。还可以设置其他属性,比方在将游标发送到数据源之前转换游标某些字段中的数据的函数。
更新、插进去和删除页面的表面险些雷同。它们许可您为更新、删除和插进去属性集指定值。关于VFP不能自动生成update语句的XML,这一点尤为主要。
运用本机数据
只管很明显CursorAdapter的目标是为了标准化和简化对非VFP数据的接见,然则您可以经由历程将DataSourceType设置为“Native”来运用它来替换Cursor。你为什么如许做?主如果倾向于未来应用程序升级;经由历程简朴地将DataSourceType变动成其他选项之一(并大概变动其他一些属性,如设置衔接信息),您可以轻松地切换到其他DBMS,如SQL Server。
当DataSourceType设置为“Native”时,VFP将疏忽DataSource。SelectCmd必须是一个SQL SELECT语句,而不是USE敕令或表达式,这意味着您老是运用相当于当地视图的语句,而不是直接运用表。您须确保VFP可找到SELECT语句中援用的任何表,因而假如这些表不在当前目次中,则须要设置途径或翻开表所属的数据库。与平常一样,假如愿望游标可更新,请确保设置更新属性(KeyFieldList、Tables、UpdateableFieldList和UpdateNameList)。
以下示例(NativeExample.prg)从TestData VFP示例数据库中的Customer表竖立一个可更新的游标:
local loCursor as CursorAdapter, ; laErrors[1] open database (_samples + 'datatestdata') loCursor = createobject('CursorAdapter') with loCursor .Alias = 'customercursor' .DataSourceType = 'Native' .SelectCmd = "select CUST_ID, COMPANY, CONTACT from CUSTOMER " + ; "where COUNTRY = 'Brazil'" .KeyFieldList = 'CUST_ID' .Tables = 'CUSTOMER' .UpdatableFieldList = 'CUST_ID, COMPANY, CONTACT' .UpdateNameList = 'CUST_ID CUSTOMER.CUST_ID, ' + ; 'COMPANY CUSTOMER.COMPANY, CONTACT CUSTOMER.CONTACT' if .CursorFill() browse tableupdate(1) else aerror(laErrors) messagebox(laErrors[2]) endif .CursorFill() endwith close databases all
运用ODBC
ODBC实际上是DataSourceType的四个设置中最直接的一个。将DataSource设置为翻开的ODBC衔接句柄,设置经常使用属性,然后挪用CursorFill来检索数据。假如您填写KeyFieldList、Tables、UpdateableFieldList和UpdateNameList,VFP将自动生成恰当的UPDATE、INSERT和DELETE语句,以便用任何变动更新后端。假如要改用存储历程,请恰当设置*Cmd、*CmdDataSource和*CmdDataSourceType属性。
下面是一个示例,取自ODBCExample.prg,它挪用Northwind数据库中的CustOrderHist存储历程,以猎取特定客户按产品销售的总单元:
local loCursor as CursorAdapter, ; laErrors[1] loCursor = createobject('CursorAdapter') with loCursor .Alias = 'CustomerHistory' .DataSourceType = 'ODBC' .DataSource = sqlstringconnect('driver=SQL Server;server=(local);' + ; 'database=Northwind;uid=sa;pwd=;trusted_connection=no') .SelectCmd = "exec CustOrderHist 'ALFKI'" if .CursorFill() browse else aerror(laErrors) messagebox(laErrors[2]) endif .CursorFill() endwith
运用ADO
运用ADO作为CursorAdapter的数据接见机制比运用ODBC有更多的问题:
- 必须将数据源设置为ADO纪录集,该纪录集的ActiveConnection属性设置为翻开的ADO衔接对象。
- 假如要运用参数化查询(这多是罕见状况,而不是检索一切纪录),则必须将其ActiveConnection属性设置为open ADO Connection对象的ADO敕令对象作为第四个参数通报给CursorFill。VFP将担任为您添补Command对象的参数鸠合(它剖析SelectCmd以查找参数),但包含参数值的变量固然必须在作用域中。
- 在数据环境中将一个CursorAdapter与ADO一同运用很简朴:可以将UseDEDataSource设置为.T。假如情愿,可以像运用CursorAdapter一样设置数据环境的DataSource和DataSourceType属性。然则,假如数据环境中有多个CursorAdapter,则此操纵不起作用。原因是DataEnvironment.DataSource援用的ADO纪录集只能包含一个CursorAdapter的数据;当为第二个CursorAdapter挪用CursorFill时,会涌现“纪录集已翻开”毛病。因而,假如您的数据环境有多个CursorAdapter,则必须将UseDEDataSource设置为.F并本身治理每一个CursorAdapter的DataSource和DataSourceType属性(或许大概运用为您治理这些属性的DataEnvironment子类)。
下面的示例代码取自ADOExample.prg,它展现了怎样在ADO敕令对象的协助下运用参数化查询检索数据。这个例子还展现了VFP 8中新的组织化毛病处置惩罚特征的运用;对ADO衔接Open要领的挪用封装在一个TRY…CATCH…ENDTRY语句捕捉要领失利时将引发的COM毛病。
local loConn as ADODB.Connection, ; loCommand as ADODB.Command, ; loException as Exception, ; loCursor as CursorAdapter, ; lcCountry, ; laErrors[1] loConn = createobject('ADODB.Connection') with loConn .ConnectionString = 'provider=SQLOLEDB.1;data source=(local);' + ; 'initial catalog=Northwind;uid=sa;pwd=;trusted_connection=no' try .Open() catch to loException messagebox(loException.Message) cancel endtry endwith loCommand = createobject('ADODB.Command') loCursor = createobject('CursorAdapter') with loCursor .Alias = 'Customers' .DataSourceType = 'ADO' .DataSource = createobject('ADODB.RecordSet') .SelectCmd = 'select * from customers where country=?lcCountry' lcCountry = 'Brazil' .DataSource.ActiveConnection = loConn loCommand.ActiveConnection = loConn if .CursorFill(.F., .F., 0, loCommand) browse else aerror(laErrors) messagebox(laErrors[2]) endif .CursorFill(.F., .F., 0, loCommand) endwith
运用XML
将XML与CursorAdapter连系运用须要一些分外的东西。以下是问题:
- 数据源属性被疏忽。
- 纵然将.F.作为第一个参数通报给CursorFill要领,也必须填写CursorSchema属性,不然将涌现毛病。
- SelectCmd属性必须设置为返回游标XML的表达式,比方用户定义函数(UDF)或对象要领称号。
- 对游标所做的变动将转换为diffgram,diffgram是一种XML,它包含变动字段和纪录的之前和以后的值,并在须要更新时安排在UpdateGram属性中。
- 为了将变动写回数据源,UpdateCmdDataSourceType必须设置为“XML”,UpdateCmd必须设置为处置惩罚更新的表达式(一样,多是UDF或对象要领)。您大概须要将“This.UpdateGram”通报给UDF,以便它可以将变动发送到数据源。
游标的XML源可以来自差别的处所。比方,可以挪用一个UDF,该UDF运用CURSORTOXML()将VFP游标转换为XML,并返回效果:
use CUSTOMERS cursortoxml('customers', 'lcXML', 1, 8, 0, '1') return lcXML
UDF可以挪用返回效果集为XML的Web效劳。下面是一个从我在本身的体系上竖立和注册的Web效劳中为我生成的自动感到示例(细节并不主要;它只是显现了一个Web效劳的示例)。
local loWS as dataserver web service loWS = NEWOBJECT("Wsclient",HOME()+"ffc_webservices.vcx") loWS.cWSName = "dataserver web service" loWS = loWS.SetupClient("http://localhost/SQDataServer/dataserver.WSDL", ; "dataserver", "dataserverSoapPort") lcXML = loWS.GetCustomers() return lcXML
它可以运用SQLXML 3.0实行存储在Web效劳器模板文件中的SQL Server 2000查询(有关SQLXML的更多信息,请接见http://msdn.microsoft.com并搜刮SQLXML)。下面的代码运用MSXML2.XMLHTTP对象经由历程HTTP从Northwind Customers表中猎取一切纪录;稍后将细致诠释这一点。
local loXML as MSXML2.XMLHTTP loXML = createobject('MSXML2.XMLHTTP') loXML.open('POST', 'http://localhost/northwind/template/' + ; 'getallcustomers.xml, .F.) loXML.setRequestHeader('Content-type', 'text/xml') loXML.send() return loXML.responseText
处置惩罚更新更加庞杂。数据源必须可以接收和运用diffgram(与SQL Server 2000一样),或许您必须本身找出变动并发出一系列SQL语句(UPDATE、INSERT和DELETE)来实行更新。
下面是一个示例(XMLExample.prg),它运用带有XML数据源的CursorAdapter。注重,SelectCmd和UpdateCmd都挪用UDF。在SelectCmd的状况下,SQL Server 2000 XML模板的称号和要检索的客户ID被通报给一个名为GetNWXML的UDF,稍后我们将议论这个UDF。关于UpdateCmd,VFP将UpdateGram属性通报给SendNWXML,我们稍后也将检察该属性。
local loCustomers as CursorAdapter, ; laErrors[1] loCustomers = createobject('CursorAdapter') with loCustomers .Alias = 'Customers' .CursorSchema = 'CUSTOMERID C(5), COMPANYNAME C(40), ' + ; 'CONTACTNAME C(30), CONTACTTITLE C(30), ADDRESS C(60), ' + ; 'CITY C(15), REGION C(15), POSTALCODE C(10), COUNTRY C(15), ' + ; 'PHONE C(24), FAX C(24)' .DataSourceType = 'XML' .KeyFieldList = 'CUSTOMERID' .SelectCmd = 'GetNWXML([customersbyid.xml?customerid=ALFKI])' .Tables = 'CUSTOMERS' .UpdatableFieldList = 'CUSTOMERID, COMPANYNAME, CONTACTNAME, ' + ; 'CONTACTTITLE, ADDRESS, CITY, REGION, POSTALCODE, COUNTRY, PHONE, FAX' .UpdateCmdDataSourceType = 'XML' .UpdateCmd = 'SendNWXML(This.UpdateGram)' .UpdateNameList = 'CUSTOMERID CUSTOMERS.CUSTOMERID, ' + ; 'COMPANYNAME CUSTOMERS.COMPANYNAME, ' + ; 'CONTACTNAME CUSTOMERS.CONTACTNAME, ' + ; 'CONTACTTITLE CUSTOMERS.CONTACTTITLE, ' + ; 'ADDRESS CUSTOMERS.ADDRESS, ' + ; 'CITY CUSTOMERS.CITY, ' + ; 'REGION CUSTOMERS.REGION, ' + ; 'POSTALCODE CUSTOMERS.POSTALCODE, ' + ; 'COUNTRY CUSTOMERS.COUNTRY, ' + ; 'PHONE CUSTOMERS.PHONE, ' + ; 'FAX CUSTOMERS.FAX' if .CursorFill(.T.) browse else aerror(laErrors) messagebox(laErrors[2]) endif .CursorFill(.T.) endwith
此代码援用的XML模板CustomersByID.XML以下所示:
<root xmlns:sql="urn:schemas-microsoft-com:xml-sql"> <sql:header> <sql:param name="customerid"> </sql:param> </sql:header> <sql:query client-side-xml="0"> SELECT * FROM Customers WHERE CustomerID = @customerid FOR XML AUTO </sql:query> </root>
将此文件放在Northwind数据库的虚拟目次中(有关设置IIS以运用SQL Server的细致信息,请参阅附录)。
这是GetNWXML的代码。它运用MSXML2.XMLHTTP对象接见Web效劳器上的SQL Server 2000 XML模板并返回效果。模板的称号(以及可选的任何查询参数)作为参数通报给此代码。
lparameters tcURL local loXML as MSXML2.XMLHTTP loXML = createobject('MSXML2.XMLHTTP') loXML.open('POST', 'http://localhost/northwind/template/' + tcURL, .F.) loXML.setRequestHeader('Content-type', 'text/xml') loXML.send() return loXML.responseText
SendNWXML看起来很类似,只是它愿望通报一个diffgram,将diffgram加载到MSXML2.DOMDocument对象中,并将该对象通报给Web效劳器,然后Web效劳器将经由历程SQLXML将其通报给SQL Server 2000举行处置惩罚。
lparameters tcDiffGram local loDOM as MSXML2.DOMDocument, ; loXML as MSXML2.XMLHTTP loDOM = createobject('MSXML2.DOMDocument') loDOM.async = .F. loDOM.loadXML(tcDiffGram) loXML = createobject('MSXML2.XMLHTTP') loXML.open('POST', 'http://localhost/northwind/', .F.) loXML.setRequestHeader('Content-type', 'text/xml') loXML.send(loDOM)
要相识其事变道理,请运转XMLExample.prg。您应当在阅读窗口中看到一条纪录(ALFKI客户)。变动某个字段中的值,然后封闭窗口并再次运转PRG。您应当看到您的变动已写入后端。
CursorAdapter 和 DataEnvironment 子类
与VFP中一般的状况一样,我竖立了CursorAdapter和DataEnvironment的子类,我将运用这些子类而不是基类。
SFCursorAdapter
SFCursorAdapter(在SFDataClasses.vcx中)是CursorAdapter的一个子类,它增加了一些附加功用:
- 它可以自动处置惩罚参数化查询;您可以将参数值定义为静态(常量值)或动态(表达式,比方“=Thisform.txtName.value”,在翻开或革新游标时盘算)。
- 它可以在游标翻开后自动竖立索引。
- 它为ADO做了一些特别的事变,比方将数据源设置为ADO纪录集,将纪录集的ActiveConnection属性设置为ADO衔接对象,以及在运用参数化查询时竖立ADO敕令对象并将其通报给CursorFill。
- 它供应了一些简朴的毛病处置惩罚(cErrorMessage属性用毛病音讯添补)。
- 它有CursorAdapter中缺乏的更新和宣布要领。
让我们来看看这个类。
Init要领竖立两个鸠合(运用新的鸠合基类,它保护事物的鸠合),一个用于SelectCmd属性大概须要的参数,另一个用于在游标翻开后应自动竖立的标记。它还设置了MULTILOCKS on,因为这是CursorAdapter游标所必须的。
with This * 竖立参数和标记鸠合 .oParameters = createobject('Collection') .oTags = createobject('Collection') * 确保 MULTILOCKS 设置为 on. set multilocks on endwith
AddParameter要领向parameters鸠合增加一个参数。向此要领通报参数的称号(该称号应与SelectCmd属性中显现的称号婚配)和可选的参数值(假如如今不通报,可以稍后运用GetParameter要领举行设置)。这段代码展现了VFP 8中的两个新特征:新的Empty类(没有PEMs),使其成为轻量级对象的抱负挑选;ADDPROPERTY()函数(其作用类似于那些没有该要领的对象的ADDPROPERTY要领)。
lparameters tcName, ; tuValue local loParameter loParameter = createobject('Empty') addproperty(loParameter, 'Name', tcName) addproperty(loParameter, 'Value', tuValue) This.oParameters.Add(loParameter, tcName)
运用GetParameter要领返回一个特定的参数对象;当您想设置要用于参数的值时,一般会运用这个要领。
lparameters tcName local loParameter loParameter = This.oParameters.Item(tcName) return loParameter
SetConnection要领用于将DataSource属性设置为所需的衔接。假如DataSourceType是“ODBC”,请通报衔接句柄。假如是“ADO”,则数据源须如果一个ADO纪录集,其ActiveConnection属性设置为翻开的ADO衔接对象,因而经由历程Connection对象,SetConnection将竖立纪录集并将其ActiveConnection设置为通报对象。
lparameters tuConnection with This do case case .DataSourceType = 'ODBC' .DataSource = tuConnection case .DataSourceType = 'ADO' .DataSource = createobject('ADODB.RecordSet') .DataSource.ActiveConnection = tuConnection endcase endwith
要竖立游标,请挪用GetData要领而不是CursorFill,因为它会自动处置惩罚参数和毛病。假如要竖立游标但不添补数据,请将.T.通报给GetData。此要领所做的第一件事是竖立私有局限的变量,这些变量的称号和值与参数鸠合中定义的参数雷同(从这里挪用的GetParameterValue要领返回参数对象的值或以“=”开头的值的求值)。接下来,假如我们运用ADO并且有任何参数,代码将竖立一个ADO Command对象并将其ActiveConnection设置为Connection对象,然后将Command对象通报给CursorFill要领;CursorAdapter请求在参数化ADO查询中运用该要领。假如我们没有运用ADO或许没有任何参数,代码只挪用cursor fill来添补游标。注重.T.被通报给CursorFill,通知它在CursorSchema被添补时运用CursorSchema(这是我愿望基类具有的行动)。假如竖立了游标,则代码挪用CreateTags要领为游标竖立所需的索引;假如没有,则挪用HandleError要领来处置惩罚发作的任何毛病。
lparameters tlNoData local loParameter, ; lcName, ; luValue, ; llUseSchema, ; loCommand, ; llReturn with This *假如我们要添补游标(而不是竖立空游标),则竖立变量来保留任何参数 *必须在这里而不是在要领中如许做,因为我们愿望它们的作用域是私有的 if not tlNoData for each loParameter in .oParameters lcName = loParameter.Name luValue = .GetParameterValue(loParameter) store luValue to (lcName) next loParameter endif not tlNoData *若运用ADO且有参数,则需一个Command对象来处置惩罚这个问题 llUseSchema = not empty(.CursorSchema) if '?' $ .SelectCmd and (.DataSourceType = 'ADO' or (.UseDEDataSource and ; .Parent.DataSourceType = 'ADO')) loCommand = createobject('ADODB.Command') loCommand.ActiveConnection = iif(.UseDEDataSource, ; .Parent.DataSource.ActiveConnection, .DataSource.ActiveConnection) llReturn = .CursorFill(llUseSchema, tlNoData, .nOptions, loCommand) else *尝试添补游标 llReturn = .CursorFill(llUseSchema, tlNoData, .nOptions) endif '?' $ .SelectCmd ... *假如我们竖立了游标,请竖立为其定义的任何标记。 *假如没有,请处置惩罚毛病。 if llReturn .CreateTags() else .HandleError() endif llReturn endwith return llReturn
Update要领很简朴:它只挪用TABLEUPDATE()尝试更新原始数据源,假如失利则挪用HandleError。
local llReturn llReturn = tableupdate(1, .F., This.Alias) if not llReturn This.HandleError() endif not llReturn return llReturn
有几种要领我们在这里不看,你可以本身搜检一下。AddTag将游标竖立后要竖立的索引的信息增加到tags鸠合,而CreateTags(从GetData挪用)在INDEX ON语句中运用该鸠合中的信息。HandleError运用AERROR()来肯定失足的处所,并将毛病数组的第二个元素放入cErrorMessage属性中。
让我们看几个运用这个类的例子。第一个(取自TestCursorAdapter.prg)从Northwind数据库的Customers表中猎取一切纪录。这段代码与用于基类CursorAdapter的代码没有太大的差别(因为没有填写CursorSchema,因而必须将.F.作为第一个参数通报给CursorFill)。
loCursor = newobject('SFCursorAdapter', 'SFDataClasses') with loCursor *衔接到SQL Server Northwind数据库并猎取客户纪录 .DataSourceType = 'ODBC' .DataSource = sqlstringconnect('driver=SQL Server;server=(local);' + ; 'database=Northwind;uid=sa;pwd=;trusted_connection=no') .Alias = 'Customers' .SelectCmd = 'select * from customers' if .GetData() browse else messagebox('Could not get the data. The error message was:' + ; chr(13) + chr(13) + .cErrorMessage) endif .GetData() endwith
下一个示例(也取自TestCursorAdapter.prg)运用SFConnectionMgr的ODBC版原本治理衔接,我们在“VFP中的数据战略:基础篇”一文中检察了该版本。它还为SelectCmd运用参数化语句,显现AddParameter要领怎样许可您处置惩罚参数,并演示怎样运用AddTag要领自动为游标竖立标记。
loConnMgr = newobject('SFConnectionMgrODBC', 'SFRemote') with loConnMgr .cDriver = 'SQL Server' .cServer = '(local)' .cDatabase = 'Northwind' .cUserName = 'sa' .cPassword = '' endwith if loConnMgr.Connect() loCursor = newobject('SFCursorAdapter', 'SFDataClasses') with loCursor .DataSourceType = 'ODBC' .SetConnection(loConnMgr.GetConnection()) .Alias = 'Customers' .SelectCmd = 'select * from customers where country = ?pcountry' .AddParameter('pcountry', 'Brazil') .AddTag('CustomerID', 'CustomerID') .AddTag('Company', 'upper(CompanyName)') .AddTag('Contact', 'upper(ContactName)') if .GetData() messagebox('Brazilian customers in CustomerID order') set order to CustomerID go top browse messagebox('Brazilian customers in Contact order') set order to Contact go top browse messagebox('Canadian customers') loParameter = .GetParameter('pcountry') loParameter.Value = 'Canada' .Requery() browse else messagebox('Could not get the data. The error message was:' + ; chr(13) + chr(13) + .cErrorMessage) endif .GetData() endwith else messagebox(loConnMgr.cErrorMessage) endif loConnMgr.Connect()
SFDataEnvironment
SFDataEnvironment(也在SFDataClasses.vcx中)比SFCursorAdapter简朴许多,但增加了一些有效的功用:
- GetData要领挪用一切SFCursorAdapter成员的GetData要领,因而没必要零丁挪用它们。
- 类似地,Requery和Update要领挪用每一个SFCursorAdapter成员的Requery和Update要领。
- 与SFCursorAdapter类似,SetConnection要领将数据源设置为ADO纪录集,并将纪录集的ActiveConnection属性设置为ADO衔接对象。然则,它也挪用UseDEDataSource设置为.F的任何SFCursorAdapter成员的SetConnection要领。
- 它供应了一些简朴的毛病处置惩罚(用毛病音讯添补cErrorMessage属性)。
- 它有一个Release要领。
GetData异常简朴:它只挪用具有该要领的任何成员对象的GetData要领。
lparameters tlNoData local loCursor, ; llReturn for each loCursor in This.Objects if pemstatus(loCursor, 'GetData', 5) llReturn = loCursor.GetData(tlNoData) if not llReturn This.cErrorMessage = loCursor.cErrorMessage exit endif not llReturn endif pemstatus(loCursor, 'GetData', 5) next loCursor return llReturn
SetConnection轻微庞杂一点:它挪用任何具有该要领且UseDEDataSource设置为.F.的成员对象的SetConnection要领,然后运用类似于SFCursorAdapter中的代码设置本身的数据源(假如任何CursorAdapter的UseDEDataSource设置为.T.)。
lparameters tuConnection local llSetOurs, ; loCursor, ; llReturn with This *挪用任何不运用数据源的CursorAdapter的SetConnection要领 llSetOurs = .F. for each loCursor in .Objects do case case upper(loCursor.BaseClass) <> 'CURSORADAPTER' case loCursor.UseDEDataSource llSetOurs = .T. case pemstatus(loCursor, 'SetConnection', 5) loCursor.SetConnection(tuConnection) endcase next loCursor *假如发明运用数据源的CursorAdapter,须要设置数据源 if llSetOurs do case case .DataSourceType = 'ODBC' .DataSource = tuConnection case .DataSourceType = 'ADO' .DataSource = createobject('ADODB.RecordSet') .DataSource.ActiveConnection = tuConnection endcase endif llSetOurs endwith
Requery和Update险些与GetData雷同,所以我们没必要省心去检察它们。
TestDE.prg显现了怎样运用SFDataEnvironment作为两个SFCursorAdapter类的容器。因为此示例运用ADO,因而每一个SFCursorAdapter都须要本身的数据源,故UseDEDataSource设置为.F。请注重,对DataEnvironment SetConnection要领的单个挪用担任为每一个CursorAdapter设置数据源属性。
loConnMgr = newobject('SFConnectionMgrADO', 'SFRemote') with loConnMgr .cDriver = 'SQLOLEDB.1' .cServer = '(local)' .cDatabase = 'Northwind' .cUserName = 'sa' .cPassword = '' endwith if loConnMgr.Connect() loDE = newobject('SFDataEnvironment', 'SFDataClasses') with loDE .NewObject('CustomersCursor', 'SFCursorAdapter', 'SFDataClasses') with .CustomersCursor .Alias = 'Customers' .SelectCmd = 'select * from customers' .DataSourceType = 'ADO' endwith .NewObject('OrdersCursor', 'SFCursorAdapter', 'SFDataClasses') with .OrdersCursor .Alias = 'Orders' .SelectCmd = 'select * from orders' .DataSourceType = 'ADO' endwith .SetConnection(loConnMgr.GetConnection()) if .GetData() select Customers browse nowait select Orders browse else messagebox('Could not get the data. The error message was:' + ; chr(13) + chr(13) + .cErrorMessage) endif .GetData() endwith else messagebox(loConnMgr.cErrorMessage) endif loConnMgr.Connect()
可重用数据类
如今我们有了CursorAdapter和DataEnvironment子类,让我们议论一下可重用的数据类。
VFP开发人员请求微软在VFP中增加的一件事是可重用的数据环境。比方,您大概有一个表单和一个报表具有完全雷同的数据设置,然则您必须手动为每一个表单和报表添补数据环境,因为数据环境是不可重用的。一些开发人员(以及险些一切的框架供应商)经由历程在代码中竖立数据环境(它们不能可视化地被子类化)并在表单上运用“loader”对象来实例化数据环境子类,使得竖立可重用的数据环境变得越发轻易。但是,这是一种杂沓,并没有协助报告。
如今,在VFP 8中,我们可以竖立两个可重用的数据类,它们可以供应从任何数据源就任何须要它们的数据源的游标,以及可重用的数据环境,后者可以托管数据类。在撰写本文时,您不能在报表中运用CursorAdapter或DataEnvironment子类,但可以经由历程编程增加CursorAdapter子类(比方在DataEnvironment的Init要领中)来应用那边的可重用性。
我们来为Northwind客户和定单表竖立数据类。起首,竖立SFCursorAdapter的一个子类CustomersCursor并设置属性,以下所示。
属性 | 值 |
Alias | Customers |
CursorSchema | CUSTOMERID C(5), COMPANYNAME C(40), CONTACTNAME C(30), CONTACTTITLE C(30), ADDRESS C(60), CITY C(15), REGION C(15), POSTALCODE C(10), COUNTRY C(15), PHONE C(24), FAX C(24) |
KeyFieldList | CUSTOMERID |
SelectCmd | select * from customers |
Tables | CUSTOMERS |
UpdatableFieldList | CUSTOMERID, COMPANYNAME, CONTACTNAME, CONTACTTITLE, ADDRESS, CITY, REGION, POSTALCODE, COUNTRY, PHONE, FAX |
UpdateNameList | CUSTOMERID CUSTOMERS.CUSTOMERID, COMPANYNAME CUSTOMERS.COMPANYNAME, CONTACTNAME CUSTOMERS.CONTACTNAME, CONTACTTITLE CUSTOMERS.CONTACTTITLE, ADDRESS CUSTOMERS.ADDRESS, CITY CUSTOMERS.CITY, REGION CUSTOMERS.REGION, POSTALCODE CUSTOMERS.POSTALCODE, COUNTRY CUSTOMERS.COUNTRY, PHONE CUSTOMERS.PHONE, FAX, CUSTOMERS.FAX |
备注:您可以运用CursorAdapter生成器完成大部分事变,迥殊是设置CursorSchema和更新属性。窍门是翻开“use connection settings in builder only”(仅在生成器中运用衔接设置)选项,填写衔接信息以竖立及时衔接,然后填写SelectCmd并运用生成器为您构建其他属性。
如今,只需您须要Northwind Customers表中的纪录,就只需运用CustomersCursor类。固然,我们还没有定义任何衔接信息,但这实际上是件功德,因为这个类没必要忧郁怎样猎取数据(ODBC、ADO或XML),以至没必要忧郁要运用什么数据库引擎(用于SQL Server、Access和新版VFP8的Northwind数据库)。
然则请注重,这个游标触及Customers表中的一切纪录。有时候,你只想要一个特定的客户。所以,让我们竖立一个CustomersCursor的子类CustomerByIDCursor。将SelectCmd变动成“select * from customers where customerid = ?pcustomerid”并将以下代码放入Init:
lparameters tcCustomerID dodefault() This.AddParameter('pCustomerID', tcCustomerID)
这将竖立一个名为pCustomerID的参数(与SelectCmd中指定的称号雷同),并将其设置为通报的恣意值。假如未通报恣意值,请运用GetParameter返回此参数的对象,并在挪用GetData之前设置其Value属性。
竖立一个类似于CustomersCursor的orderscorsor类,只是它从Orders表中检索一切纪录。然后竖立一个OrdersForCustomerCursor子类,该子类只检索特定客户的定单。将SelectCmd设置为“select * from orders where customerid = ?pcustomerid”,并将与CustomerByIDCursor雷同的代码放入Init(因为它是雷同的参数)。
要测试其效果,请运转TestCustomersCursor.prg。
示例:Form
如今我们有了一些可重用的数据类,来用一下它们。起首,让我们竖立一个名为CustomersAndOrdersDataEnvironment的SFDataEnvironment子类,它包含CustomerByIDCursor和OrdersForCustomerCursor类。将AutoOpenTables设置为.F(因为我们须要在翻开表之前设置衔接信息),并将CursorAdapter和UseDEDataSource设置为.T。如今可以以某种情势运用此数据环境来显现有关特定客户的信息,包含其定单。
让我们竖立如许一个表单。竖立一个名为CustomerOrders.scx的表单(它包含在本文档附带的示例文件中),将DEClass和DEClassLibrary设置为CustomersAndOrdersDataEnvironment,以便我们运用可重用的数据环境。将以下代码放入Load要领中:
#define ccDATASOURCETYPE 'ADO' with This.CustomersAndOrdersDataEnvironment *设置数据环境数据源 .DataSourceType = ccDATASOURCETYPE *假如我们运用ODBC或ADO,请竖立一个衔接治理器 *并翻开衔接到Northwind数据库的衔接 if .DataSourceType $ 'ADO,ODBC' This.AddProperty('oConnMgr') This.oConnMgr = newobject('SFConnectionMgr' + ccDATASOURCETYPE, ; 'SFRemote') with This.oConnMgr .cDriver = iif(ccDATASOURCETYPE = 'ADO', 'SQLOLEDB.1', ; 'SQL Server') .cServer = '(local)' .cDatabase = 'Northwind' .cUserName = 'sa' .cPassword = '' endwith if not This.oConnMgr.Connect() messagebox(oConnMgr.cErrorMessage) return .F. endif not This.oConnMgr.Connect() *假如我们运用ADO,每一个游标都必须有本身的数据源 if .DataSourceType = 'ADO' .CustomerByIDCursor.UseDEDataSource = .F. .CustomerByIDCursor.DataSourceType = 'ADO' .OrdersForCustomerCursor.UseDEDataSource = .F. .OrdersForCustomerCursor.DataSourceType = 'ADO' endif .DataSourceType = 'ADO' *将数据源设置为衔接 .SetConnection(This.oConnMgr.GetConnection()) *假如运用的是XML,请变动SelectCmd以挪用GetNWXML函数 else .CustomerByIDCursor.SelectCmd = 'GetNWXML([customersbyid.xml?' + ; 'customerid=] + pCustomerID)' .CustomerByIDCursor.UpdateCmdDataSourceType = 'XML' .CustomerByIDCursor.UpdateCmd = 'SendNWXML(This.UpdateGram)' .OrdersForCustomerCursor.SelectCmd = 'GetNWXML([ordersforcustomer.' + ; 'xml?customerid=] + pCustomerID)' .OrdersForCustomerCursor.UpdateCmdDataSourceType = 'XML' .OrdersForCustomerCursor.UpdateCmd = 'SendNWXML(This.UpdateGram)' endif .DataSourceType $ 'ADO,ODBC' *指定将从CustomerID文本框中添补游标参数的值 loParameter = .CustomerByIDCursor.GetParameter('pCustomerID') loParameter.Value = '=Thisform.txtCustomerID.Value' loParameter = .OrdersForCustomerCursor.GetParameter('pCustomerID') loParameter.Value = '=Thisform.txtCustomerID.Value' *竖立空游标并在失利时显现毛病音讯 if not .GetData(.T.) messagebox(.cErrorMessage) return .F. endif not .GetData(.T.) endwith
这看起来像许多代码,但个中大部分是为了演示目标,以许可切换到差别的数据接见机制。
此代码竖立一个衔接治理器来处置惩罚衔接(ADO、ODBC或XML),详细取决于ccDATASOURCETYPE常量,您可以变动该常量以尝试每一个机制。关于ADO,因为每一个CursorAdapter都必须有本身的数据源,因而为每一个CursorAdapter设置UseDEDataSource和DataSourceType属性。然后,代码挪用SetConnection要领来设置衔接信息。关于XML,SelectCmd、UpdateCmdDataSourceType和UpdateCmd属性必须如前所述举行变动。接下来,代码运用两个CursorAdapter对象的GetParameter要领将pCustomerID参数的值设置为表单中文本框的内容。注重在值中运用“=”;这意味着每次须要时都会对Value属性求值,因而我们基础上有一个动态参数(当用户在文本框中键入时,保留将参数不停变动成当前值的须要)。末了,挪用GetData要领来竖立空游标,以便控件的数据绑定可以事变。
在表单上安排一个文本框并将其命名为txtCustomer,将以下代码放入其Valid要领中:
with Thisform .CustomersAndOrdersDataEnvironment.Requery() .Refresh() endwith
这将致使在输入客户ID时从新查询游标和革新控件。
在表单上安排一个标签,放在文本框旁边,并将其标题设置为“客户ID”。
将CompanyName、ContactName、Address、City、Region、PostalCode和Country字段从DataEnvironment中的Customers游标拖动到表单中,以竖立这些字段的控件。然后在Orders游标中挑选OrderID、EmployeeID、OrderDate、RequiredDate、ShippedDate、ShipVia和Freight字段,并将它们拖到表单中以竖立网格(Grid--译者注)。
就如许子。运转表单并输入“ALFKI”作为客户ID。当您在文本框中挑选选项卡时,您应当会看到客户地点信息和定单。尝试变动有关客户或定单的内容,然后封闭表单,再次运转它,然后再次输入“ALFKI”。您应当看到,您所做的变动已写入后端数据库,而无需您支付任何勤奋。
很酷吧?这比基于当地表或视图竖立表单要简朴许多。更好的要领是,尝试将ccDATASOURCETYPE常量变动成“ADO”或“XML”,并注重表单的表面和事变体式格局完全雷同。这就是CursorAdapters的要点!
示例:Report
我们试一个Report。此处议论的示例取自此文档附带的CustomerOrders.frx。这里最大的问题是,与表单差别,我们不能通知报表运用DataEnvironment子类,也不能在DataEnvironment中删除CursorAdapter子类。因而,我们必须在报表中放入一些代码,以便将CursorAdapter子类增加到数据环境中。只管将此代码放入报表数据环境的BeforeOpenTables事宜中似乎是合乎逻辑的,但实际上这不会起作用,因为我不明白为什么,在预览报表时,BeforeOpenTables会在每一个页面上引发。所以,我们将把代码放入Init要领中。
#define ccDATASOURCETYPE 'ODBC' with This set safety off *设置数据环境数据源 .DataSourceType = ccDATASOURCETYPE *为客户和定单竖立CursorAdapter对象 .NewObject('CustomersCursor', 'CustomersCursor', 'NorthwindDataClasses') .CustomersCursor.AddTag('CustomerID', 'CustomerID') .NewObject('OrdersCursor', 'OrdersCursor', 'NorthwindDataClasses') .OrdersCursor.AddTag('CustomerID', 'CustomerID') *若运用ODBC或ADO,请竖立一个衔接治理器 *并翻开衔接到Northwind数据库的衔接 if .DataSourceType $ 'ADO,ODBC' .AddProperty('oConnMgr') .oConnMgr = newobject('SFConnectionMgr' + ccDATASOURCETYPE, ; 'SFRemote') with .oConnMgr .cDriver = iif(ccDATASOURCETYPE = 'ADO', 'SQLOLEDB.1', ; 'SQL Server') .cServer = '(local)' .cDatabase = 'Northwind' .cUserName = 'sa' .cPassword = '' endwith if not .oConnMgr.Connect() messagebox(.oConnMgr.cErrorMessage) return .F. endif not .oConnMgr.Connect() *假如运用ADO,每一个游标都必须有本身的数据源 if .DataSourceType = 'ADO' .CustomersCursor.UseDEDataSource = .F. .CustomersCursor.DataSourceType = 'ADO' .CustomersCursor.SetConnection(.oConnMgr.GetConnection()) .OrdersCursor.UseDEDataSource = .F. .OrdersCursor.DataSourceType = 'ADO' .OrdersCursor.SetConnection(.oConnMgr.GetConnection()) else .CustomersCursor.UseDEDataSource = .T. .OrdersCursor.UseDEDataSource = .T. .DataSource = .oConnMgr.GetConnection() endif .DataSourceType = 'ADO' .CustomersCursor.SetConnection(.oConnMgr.GetConnection()) .OrdersCursor.SetConnection(.oConnMgr.GetConnection()) *若运用XML,请变动SelectCmd以挪用GetNWXML函数 else .CustomersCursor.SelectCmd = 'GetNWXML([getallcustomers.xml])' .CustomersCursor.DataSourceType = 'XML' .OrdersCursor.SelectCmd = 'GetNWXML([getallorders.xml])' .OrdersCursor.DataSourceType = 'XML' endif .DataSourceType $ 'ADO,ODBC' *猎取数据并在失利时显现毛病音讯 if not .CustomersCursor.GetData() messagebox(.CustomersCursor.cErrorMessage) return .F. endif not .CustomersCursor.GetData() if not .OrdersCursor.GetData() messagebox(.OrdersCursor.cErrorMessage) return .F. endif not .OrdersCursor.GetData() *设置从客户到定单的关联 set relation to CustomerID into Customers endwith
此代码看起来与窗体的代码类似。一样,大多数代码是处置惩罚差别的数据接见机制。然则,另有一些分外的代码,因为我们不能运用DataEnvironment子类,必须本身编写行动代码。
如今,我们怎样方便地把字段放在Report上?因为CursorAdapter在设想时不存在于数据环境中,因而我们不能将字段从它们拖到Report中。这里有一个提醒:竖立一个PRG来竖立游标并将其留在作用域中(经由历程挂起或使CursorAdapter对象公然),然后运用Quick Report函数将具有恰当大小的字段放在Report上。
在CUSTOMERS.CUSTOMERID上竖立一个组并选中“在新页面上启动每一个组”。然后将Report规划为类似于以下内容:
XMLAdapter
除了CursorAdapter以外,VFP 8另有三个新的基类来革新VFP对XML的支撑:XMLAdapter、XMLTable和XMLField。XMLAdapter供应了一种在XML和VFP游标之间转换数据的要领。它的功用比CURSORTOXML()和XMLTOCURSOR()函数多许多,包含支撑分层XML和运用那些函数不支撑的XML范例(如ADO.NET数据集)的功用。XMLTable和XMLField是子对象,它们供应微调XML数据的情势的才能。另外,XMLTable另有一个ApplyDiffgram要领,它许可VFP运用updategrams和diffgrams,这是VFP 7中缺乏的。
为了让您相识它的功用,我竖立了一个返回ADO.NET数据集的ASP.NET Web效劳,然后运用VFP中的XMLAdapter对象来运用该数据集。如今我做到了。
起首,在Visual Studio.NET中,我将Northwind Customers表从效劳器资源治理器拖到一个名为NWWebService的新ASP.NET Web效劳项目中。这会自动竖立两个对象,SQLConnection1和SQLDataAdapter1。然后,我将以下代码增加到现有生成的代码中:
<WebMethod()> Public Function GetAllCustomers() As DataSet Dim loDataSet As New DataSet() Me.SqlConnection1.Open() Me.SqlDataAdapter1.Fill(loDataSet) Return loDataSet End Function
我构建该项目是为了在NWWebService虚拟目次(VS.NET自动为我竖立)中生成恰当的Web效劳文件。
为了在VFP中运用这个Web效劳,我运用IntelliSense治理器注册了一个名为“Northwind.NET”的Web效劳,指向“http://localhost/NWWebService/NWWebService.asmx?WSDL”作为WSDL文件的位置。然后我竖立了以下代码(在XMLAdapterWebService.prg中)来挪用Web效劳并将ADO.NET数据集转换为VFP游标。
local loWS as Northwind.NET, ; loXMLAdapter as XMLAdapter, ; loTable as XMLTable *从.NET Web效劳猎取.NET数据集 loWS = NEWOBJECT("Wsclient",HOME()+"ffc_webservices.vcx") loWS.cWSName = "Northwind.NET" loWS = loWS.SetupClient("http://localhost/NWWebService/NWWebService.asmx" + ; "?WSDL", "NWWebService", "NWWebServiceSoap") loXML = loWS.GetAllCustomers() *竖立一个XMLAdapter并加载数据 loXMLAdapter = createobject('XMLAdapter') loXMLAdapter.XMLSchemaLocation = '1' loXMLAdapter.LoadXML(loXML.Item(0).parentnode.xml) *假如成功地加载了XML,那末从每一个表对象竖立并阅读一个游标 if loXMLAdapter.IsLoaded for each loTable in loXMLAdapter.Tables loTable.ToCursor() browse use next loTable endif loXMLAdapter.IsLoaded
注重,为了运用XMLAdapter,您须要在体系上装置MSXML 4.0效劳包1或更高版本。您可以从MSDN网站下载(http://MSDN.microsoft.com并搜刮MSXML)。
总结
我认为CursorAdapter是VFP 8中最大和最令人兴奋的加强之一,因为它供应了一个一致且易于运用的长途数据接口,而且它许可我们竖立可重用的数据类。我置信一旦你用它来事变,你会发明他们和我一样令人兴奋。
作者引见:
Doug Hennig是Stonefield Systems Group Inc.的合作伙伴。他是获奖的Stonefield数据库东西包(SDT)的作者和获奖的Stonefield查询的配合作者。他是《黑客视觉FoxPro 7.0指南》的合著者(与Tamar Granor、Ted Roche和Della Martin一同)和《视觉FoxPro 7.0的新特征》的合著者(与Tamar Granor和Kevin McNeish一同),均来自Hentzenwerke出版社,在Pinnacle Publishing的Pros Talk VisualFoxPro系列中,“VisualFoxPro数据字典”的作者。他在FoxTalk上写了每个月的“可重用东西”专栏。他是《黑客指南》和《基础知识》的手艺编辑,这两本书都来自亨森沃克出版社。自1997年以来,道格在每次微软FoxPro开发者大会(DevCon)以及北美各地的用户整体和开发者大会上都宣布过演讲。他是微软最有代价的专业人士(MVP)和认证专业人士(MCP)。
附录:设置SQL Server 2000 XML接见存取
另文,本文略……
.NET Core之单元测试(四):Fluent Assertions的使用