IT教程 ·

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数据库容器作为数据源;在这类状况下,生成的代码将确保数据库是翻开的(也可以运用自在表作为数据源)。

VFP的数据战略:高等篇 IT教程 第1张

Cursors”页面许可您保护DataEnvironmentCursorAdapter成员(游标对象不会在生成器中显现,也不能增加它们)。Add按钮许可您向DataEnvironment增加CursorAdapter子类,而New则竖立一个新的基类CursorAdapterRemove删除Select CursorAdapterBuilder为所选CursorAdapter挪用CursorAdapter Builder。您可以变动CursorAdapter对象的称号,但关于任何其他属性,都须要CursorAdapter生成器。

VFP的数据战略:高等篇 IT教程 第2张

 

从快速菜单中挑选Builder也可以挪用CursorAdapter生成器。“Properties”页显现对象的类和称号(只需在从DataEnvironment中调出生成器时才变动称号,因为它对CursorAdapter子类是只读的)、它将竖立的游标的别号、是不是应当运用DataEnvironment的数据源以及衔接信息(假如没有)。与DataEnvironment生成器一样,假如挑选“运用DSN”或“运用衔接字符串”,CursorAdapter生成器将生成代码以竖立所需的衔接(在本例中是CursorFill要领)。

VFP的数据战略:高等篇 IT教程 第3张

“数据接见”页许可您指定SelectCmd、CursorSchema和其他属性。假如您指定了衔接信息,可以单击SelectCmd的Build按钮来显现Select Command Builder,如许就可以轻松地竖立SelectCmd。

VFP的数据战略:高等篇 IT教程 第4张

Select敕令生成器简化了构建一个简朴的Select语句的事变。从“表格”下拉列表中挑选所需的表格,然后将响应的字段移到选定的一侧。关于本机数据源,可以向“表”组合框中增加表(比方,假如愿望运用余暇表)。挑选OK时,SelectCmd将添补恰当的SQL SELECT语句。

VFP的数据战略:高等篇 IT教程 第5张

单击游标情势的“生成”按钮,自动为您填写此属性。为了使其事变,生成器实际上竖立了一个新的CursorAdapter对象,恰当地设置了属性,并挪用CursorFill来竖立游标。假如您没有到数据源的及时衔接,或许CursorFill因为某种原因(比方无效的SelectCmd)失利,那末这明显行不通。
运用“自动更新”页设置VFP自动为数据源生成更新语句所需的属性。Tables属性是从SelectCmd中指定的表自动添补的,fields网格是从CursorSchema中的字段添补的。与视图设想器一样,可以经由历程搜检网格中的响应列来挑选哪些是症结字段,哪些字段是可更新的。还可以设置其他属性,比方在将游标发送到数据源之前转换游标某些字段中的数据的函数。

VFP的数据战略:高等篇 IT教程 第6张

更新、插进去和删除页面的表面险些雷同。它们许可您为更新、删除和插进去属性集指定值。关于VFP不能自动生成update语句的XML,这一点尤为主要。

VFP的数据战略:高等篇 IT教程 第7张

运用本机数据

只管很明显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的使用

参与评论