You are on page 1of 15

Session E-CAD

Cursor adapter - powerful tool to encapsulate data layer in N-tier applications

Venelina Jordanova

Introduction
Developing wide range of applications we always need to consider not only user interface, but also data model and data access. Designing data layer the developer has to choose the best database platform for the application, but he has always to keep in mind that the application which will proceed a few thousand records in single user mode in next years could expand to a large scale application, handling hundreds of thousands records. In such case you are often enforced to migrate application data toward more vigorous data storage. This often can lead to a need to fully rewrite applications data access. The approach used to access data is basic matter to avoid such fundamental application amendments, when data storage needs to be changed. In this session we will examine how CursorAdapter class can help developers to organize applications data access in such way, so future data storage changes (and respectively data source changes) to have minimal impact on it.

Cursor adapter - powerful tool 2003 Venelina Jordanova

(Group DATA)

10th European Visual FoxPro DevCon 2003 E-CAD 1

Using CursorAdapter class


The CursorAdapter acts as a middleware between source of data and a VFP cursor. It is responsible for creation cursors, so your business logic and user interface layers use these cursors and do not care about the way in which data is retrieved. I list below some most important properties, methods and events of the class. DataSourceType is the property that determines how data will be retrieved. It sets the overall behavior of the class. DataSource property is used when DataSourceType is set to ODBC or ADO. In case you use an ODBC DataSourceType, you have to set DataSource to a valid ODBC connection handle. You can use SQLConnect() or SQLStringConnect() functions to establish an ODBC connection. If you use ADO DataSourceType, DataSource property must be set to an ADODB.RecordSet object and its ActiveConnection property, in turn, must be set to a valid open ADODB.Connection. These objects you have to be created and initialized manually. You can also use the CursorAdapter Builder, which will write a lot of necessary code for you. Alias property holds the name of the resultant cursor. You always have to keep in mind that the cursor is associated with the CursorAdapter object and exists along with it. So you have to ensure that appropriate CursorAdapter object exists in order to use the created cursor. The property UseDEDataSource gives you a possibility to use DataEnvironments properties DataSourceType and DataSource. You can leave blank the correspondent CursorAdapter properties, when you set this property to True. SelectCmd is a command sent to data storage to retrieve data. It must conform syntax of the particular data storage. If you plan to use different data source types using common CursorAdapter classes, you must conform their syntax and use appropriate commands or write different code for every particular case. The CursorSchema property must contain description of the structure of the resultant cursor in format appropriate for CREATE CURSOR command. You can leave this property blank, but in that case you cannot use the cursors structure at design time. Another helpful feature of CursorSchema is that you can force data type conversions between data source types and cursor fields types (for example datetime fields to date data type). Note that the length of the schema cannot exceed 255 characters if you want to set this property directly. If you need to store longer schema string you have to make this in code. Tables, KeyFieldList, UpdatableFieldList and UpdateNameList are required properties if you want the CursorAdapter to automatically update the data. The base difference between last two mentioned properties is that UpdatableFieldList contains names of fields that will be updated back to the data source and UpdateNameList holds a list that matches cursors fields and tables fields. Keep in mind that to be sent properly your updates to data storage, you always have to include the primary key field names in the UpdateNameList. And because these are key fields and you do not want it to be updated, you will not include them in UpdatableFieldList. The ConversionFunc property specifies conversion functions that will be applied when automatic updating is performed. *Cmd, *CmdDataSource, *CmdDataSourceType properties give you more control over the data updates. CursorFill(lUseCursorSchema, lNoData, nOptions, Source) method creates the cursor and fills it with data. When lUseCursorSchema is True, it uses the schema in the CursorSchema property to create the cursor. Otherwise it creates the cursor using the data types as normally determined by Visual FoxPro. If lNoData parameter is True an empty cursor will be created. The parameter nOptions specifies additional flags for creating the cursor. These values are also used for the CursorRefresh method. The Source parameter holds a reference to an ADO Command or an open RecordSet object. CursorRefresh() method re-executes the select command. CursorAttach(cAlias, lInheritCursorProperties) and CursorDetach() methods allow you to attach an existing cursor to a CursorAdapter object or to free the cursor associated with the CursorAdapter. Attaching a cursor sets it under CursorAdapter control and the CursorAdapter is responsible for further updates to data source and closing the cursor when CursorAdapter is destroyed. If you need a cursor to exist after CursorAdapter is destroyed or you want it no longer to be under CursorAdapters control you can free the cursor using CursorDetach method. Before* and After*() events are very helpful if you need to control the behavior of CursorAdapter object more deeply. On before events you can change the SQL command that will be executed and even cancel the action.
10th

European Visual FoxPro DevCon 2003 2 E-CAD

(Group DATA)

Cursor adapter - powerful tool 2003 Venelina Jordanova

You can also use AfterCursorFill event to create necessary indexes. All of insert and update events receive parameters that give you information what happened in the data. cFldState parameter field and row state as you would receive from GETFLDSTATE(-1) function. To make easier work with new CursorAdapter class VFP 8 includes a new CursorAdapter Builder. It helps you quick to set CursorAdapters properties and also generates necessary code for you. In addition to this we also have a new DataEnvironment Builder that helps to set Data Source properties for the DataEnvironment. This is also the place, where you add CursorAdapter objects to DataEnvironment. This will be discussed later in this article.

Sample application
An important part of this article is a small sample application that is designated to show how CursorAdapter class can save your time and efforts when migrating data between different data storage platforms. The sample application consists of a form that combines presentation and business logic layers and a set of data access classes based on CursorAdapter class. Used database is well-known Northwind database, as far as it comes with Visual FoxPro 8 and MS SQL Server installations. The form presents orders and detail lines for particular order. It uses updateable cursors created from Orders and OrderDetails tables and a set of lookup cursors corresponding to Employee, Customers and Shippers tables. DataAccess is a class library that contains several CursorAdapter classes used to access applications data.

Figure 1. Data access classes

To run the application you need to access Northwind database for every different data source type that you want to use. For Native VFP data it is located in Northwind folder under _samples path. It also ships as a sample database with MS SQL Server and MS Access.

Developing data access classes


Underlying base class
The ground of well-designed data access is thoroughly planed structure of classes. Specifics of every level have to be encapsulated in such way, so any necessary changes not to enforce changes in inherited classed. We will base our data access on one underlying class, which will be responsible for common data access. This base class will be changed later when it is necessary to access different data sources. You can set the properties of the class yourself or using the builder. CursorAdapter Builder generates code to build connection and also fills most of the properties that you need to use. Our underlying class (see Figure 1) will hold connection information that will be common for all classes that will inherit it and have dummy cursor name, which will be overwritten in every particular subclass. In builder you can also set whether CursorAdapter will use DataEnvironments data source. In this case we will not use it and here we must fill connection information.

Cursor adapter - powerful tool 2003 Venelina Jordanova

(Group DATA)

10th European Visual FoxPro DevCon 2003 E-CAD 3

In the beginning our application will use VFP Native data. In the Data source type drop-down list we choose Native and then set the Database. For some reasons you may not want the Builder to generate code for the connection. Then connection setting will be used temporarily only in the builder. In the Data Access page Data fetching and Buffering mode properties have to be set and all other Data access properties are left blank. All of them will be further defined for every particular subclass.

Figure 2. Defining the base data access class caBaseAccess

Data access classes


For our sample application we will create two CursorAdapter classes that will read and update data and another four CursorAdapter classes that will be responsible to create lookup cursors, all of them based on caBaseAccess. Lets name the CursorAdapter class that will retrieve Orders data caOrders. It is based on caBaseAccess and inherits all its properties.

Figure 3. Inheriting caBaseAccess

10th

European Visual FoxPro DevCon 2003 4 E-CAD

(Group DATA)

Cursor adapter - powerful tool 2003 Venelina Jordanova

Setting properties related to data access


All properties concerning data source must keep their default values. When in the future data access needs to be changed these properties will be changed only in the underlying class. For this newly created class we have to set table specific properties: Select command, Cursor Schema, Tables UpdateNameList and UpdatableFieldList. You can also use the builder to generate the code. In the Data Access page (Figure 4) we can specify the Select command and Cursor Schema.

Figure 4. Data Access page is used to specify Select command and Cursor Schema along with Data fetching and Buffering mode.

The Select Command Builder (Figure 5) can help us easy and fast to create simple SELECT statements.

Figure 5. Select Command Builder provides visual interface for creating SQL SELECT statement

The necessary table has to be chosen from the drop-down list and you can move the fields that you need on the right side, where selected fields are listed. In the same way as defining views, you can also move the first available choice * that means that all fields from the source table will be selected. If you use Native data source type, you are also allowed to add additional tables by clicking the Add Tables button. Often this could be free tables that are not included in the database. Clicking on OK will build the appropriate SELECT statement. If you click on Build button for the Cursor Schema it is automatically generated using the specified Select command. In this moment the builder instantiates a cursor adapter and calls its CursorFill method to populate the cursor. In order to work this feature needs to have access to your data otherwise the cursor could not be created. You can also write the Cursor Schema yourself. Cursor adapter - powerful tool 2003 Venelina Jordanova (Group DATA) 10th European Visual FoxPro DevCon 2003 E-CAD 5

If we used the Select Command Builder, Tables property is already filled on the Auto-Update page (see Figure 6). Send updates option is set by default. If you want data not to be sent back to data source you can uncheck it. Auto-update checkbox determines whether CursorAdapter will automatically generate update statements. In fields grid we see fields mapping (also automatically generated) and we have to choose key fields (key symbol column) and fields that will be automatically updated (pencil symbol column).

Figure 6. Auto-Update page is used to specify how update statements will be generated.

Here we can also define conversion functions, which will be used for certain fields transformation before to send data to data source. You can also choose which values will be used for automatically WHERE clause generation. Finally we have to write code that performs additional actions according application needs. For example in the AfterCursorFill event we will write code to create necessary indexes. These indexes will be used later in forms to ensure proper data viewing in user interface forms or for building relationships between cursors. It is also possible here to perform some calculations or data conversions that cannot be performed at data storage side. An example for such calculation is a resultant field containing the number of overdue days for a bill (or an invoice). When you access Native data and your fields are of DATE data type this can be as simple expression as:
SELECT InvoiceId, InvoiceDate, iif(DATE() > DueDate, DATE() DueDate, 0) as OverdueDays from Invoice

When your fields are of DATETIME data type command will look like this:
SELECT InvoiceId, InvoiceDate, iif(DATE() > TTOD(DueDate), DATE() TTOD(DueDate), 0) as OverdueDays
10th

European Visual FoxPro DevCon 2003 6 E-CAD

(Group DATA)

Cursor adapter - powerful tool 2003 Venelina Jordanova

from Invoice

Finally if you access a SQL Database trough ODBC data source your select command:
SELECT InvoiceId, InvoiceDate, case when DATEDIFF(dd, DueDate, GETDATE()) > 0 then DATEDIFF(dd, DueDate, GETDATE()) else 0 end as OverdueDays from Invoice

To apply such functionality you will either need to write complex code to assign different values to SelectCmd depending of DataSourceType or you can perform data manipulation in AfterCursorFill event. In this case the SelectCmd will look in this way:
SELECT InvoiceId, InvoiceDate, DueDate, 0 as OverdueDays from Invoice

Cursor schema will determine the scale of resultant OverdueDays field:


INVOICEID I, INVOICEDATE D, INVOICEDATE D, OVERDUEDAYS N(3)

This will create an empty field where in AfterCursorFill you can store calculated number of days later using similar code:
CurrentDate = DATE() RELPACE ALL OverdueDays WITH IIF(DueDate > CurrentDate, DueDate - CurrentDate, 0)

Which one of the above approaches is better for your application depends on several factors. It depends on type and complexity of calculations that you need to perform, on amount of processed data rows. For some purpose you may prefer to write simple data conversions at the client side, but for heavy data processing you might prefer to use server side stored procedures. Concerning accessing different data sources you could prefer to processes data using common FoxPro code independent of data storage specifics. However, when complex online analyzing processing on large data is needed stored procedures that are optimized and cached at server side must always be considered.

Handle update conflicts


Not long ago developers needed to append to DELETE and UPDATE commands additional statements to force error rising if a conflict occurs. When Key and modified fields are used in WHERE clause and more than one user modify one and the same record and same fields, the user that will commit his changes later will not succeed to write them into the database, because no records will correspond to generated WHERE clause. In same time TABLEUPDATE() function always returns .T. because no error were encountered. The same problem can appear when deleting records. Starting with VFP 8 Service Pack 1 developers have additional properties for better update conflicts handling. Two new properties are added to CursorAdapter class, which are designated to determine how data updating errors will be processed. The ConflictCheckType property specifies how the conflict checks are handled during an update or delete operation. You have four different alternatives Value 0 1 Description Do not perform check. (Default) In a single row update mode, check for update conflicts during a SQL UPDATE or DELETE operation. If conflict occurs, specifically, when less than one record is affected by any command specified by UpdateCmd or DeleteCmd property, return error "Update conflict (Error 1585)". In a single row update mode, check for key uniqueness during a SQL UPDATE or DELETE operation. If more than one record is affected by any command specified by the UpdateCmd or DeleteCmd property, return message "Warning: The key defined by the KeyField property for table "alias" is not unique. (Error 1495)" Perform checks as specified by setting 1 and 2. Append custom command specified by ConflictCheckCmd property to the commands in the UpdateCmd and DeleteCmd properties.

3 4

Cursor adapter - powerful tool 2003 Venelina Jordanova

(Group DATA)

10th European Visual FoxPro DevCon 2003 E-CAD 7

If the ODBC driver or OLE DB Provider is unable to provide required functionality or if the functionality is disabled, settings 1, 2, and 3 for ConflictCheckType might fail. For example is SET NOCOUNT ON in SQL Server. In this case no information about affected records is available. If you set ConflictCheckType to 4 CursorAdapter uses ConflictCheckCmd property. Its value is appended to the commands specified by the UpdateCmd and DeleteCmd properties for checking update or delete conflicts. For example when working with Native data source you can write a function that checks conflicts using _tally system variable. If your data storage is SQL Server, you can use @@ROWCOUNT and @@ERROR system functions like this:
ConflictCheckCmd="IF @@ERROR=0 AND @@ROWCOUNT!=1 RAISERROR (' Update conflict!', 16, 1)"

Customizing Select statement


In the same way we define all other CursorAdapter classes that we have planed to use. For those of them that are proposed to be used as lookup cursors we can set that updates will not be send. Furthermore we can use calculated fields in select commands. For example lets plan to have a drop-down combo box that will be used to choose employee for particular order. If we use Select command builder as usual (see Figure 7), we will have two separate fields for FirstName and LastName columns.

Figure 7. Using Select Command Builder to construct select statement

When we present them in a drop-down list it is more suitable to have one common column EmployeeName that will be resultant of FirstName and LastName. To accomplish this, we will amend the generated select statement in that way:
select RTRIM(EMPLOYEES.FIRSTNAME) + " " + RTRIM(EMPLOYEES.LASTNAME) as EmployeeName, EMPLOYEES.EMPLOYEEID from EMPLOYEES

When we change the select command we have to press Build Schema button again the builder will automatically generate correct cursor schema. (Figure 8)

10th

European Visual FoxPro DevCon 2003 8 E-CAD

(Group DATA)

Cursor adapter - powerful tool 2003 Venelina Jordanova

Figure 8. Customizing Select statement to suit particular requirements

It is very important when you plan to use different data sources to write SELECT statement compliant with all of them. In this particular case I used RTRIM function instead of TRIM or ALLTRIM functions, because it is a common for VFP and T-SQL and when we switch to SQL Server data source in the future this CursorAdapter class will remain useful without any changes in it. Unfortunately amending Select command in this way does will violate Select Command Builder and you cannot invoke it again and add new fields. So you can amend Select command when you will no longer need Select Command Builder help.

Developing user interface and business logic layers


In our sample application these two layers are unified to simplify this part, as our main goal here is to create a portable data layer. In your real-life application you will split these layers according to your needs and application architecture. Lets start with creating an empty form and name it frmOrders. You can use the well-known feature to add controls on the form by simply drag fields from DataEnvironment cursors onto the form. For this purpose we have to create objects based on our data access classes into the DataEnvironment of the form. We will make this by using the DataEnvironment Builder. As far as we want to encapsulate data layer in separate classes, we will not use DataEnvironments data source properties. Data Source page can remain blank. On Cursors page we will create all CursorAdapter objects necessary for the form. There are four buttons below Cursor Adapters list that allow you to add a new object based on a specified class or to create a new CursorAdapter object, as well as to remove the object or to invoke Builder for the selected CursorAdapter. Add button invokes Open dialog where you should choose CursorAdapter class to be used for creating new CursorAdapter object (Figure 9).

Cursor adapter - powerful tool 2003 Venelina Jordanova

(Group DATA)

10th European Visual FoxPro DevCon 2003 E-CAD 9

Figure 9. Adding CursorAdapter class to forms DataEnvironment

After choosing the class, a Builder form is invoked and you can change setting for the newly created object. In this way we create CursorAdapter objects for all data needed for the form and associated cursors are visible in the DataEnvironment Designer (Figure 10.)

Figure 10. DataEnvironment of frmOrders

Here, in the DataEnvironment we can also write code in CursorAdapter objects events to prepare forms specific indexes and other actions that related to the particular form. For this sample application I choose to load all data at once. In this case it is necessary in the forms Init method to establish a relationship between crsOrders cursor and crsOrderDetails cursor. In case you will need to proceed large amount of data, loading entire table content in cursors is not a good solution. In such case you could retrieve at whole only parent table (crsOrders) and retrieve child records (crsOrderDetails) for every parent record. You could even decide to retrieve Orders row by row and load off clients computer resources. For this type of data retrieval you should use parameterized queries. Parameters are passed in a way that you already know from pass-trough queries. Now after we have created all necessary CursorAdapter objects in the forms DataEnvironment, it is fast and easy to create controls on the form just by dragging fields onto it. On Figure 11 you can see how the form looks like after creating controls and adding a navigation bar as well as a few drop-down combo boxes that are designated to give users an easier way to fill customer, shipper and employee data. For same purpose in the grdOrderdDtails grid is also added a column colProductName.

10th

European Visual FoxPro DevCon 2003 10 E-CAD

(Group DATA)

Cursor adapter - powerful tool 2003 Venelina Jordanova

Figure 11. Form designer - frmOrders

And finally we have to add code to fill OrderId field in crsOrderDetails, to ensure referential integrity when adding new detail records in the grid.

Data updating considerations


Now our form can load data and navigate trough data records. We can run it to ensure that it runs correctly. At this stage all data changes that a user makes using our form are still hold only in the cursors. Its time to manage saving data back to the data storage! Updating data for existing orders is straight forward just use TABLEUPDATE() function and all changes are committed. The more complicated task is to manage newly added orders as far as OrderId field is autoincrementing (identity in SQL Server). So, in this case the CursorAdapter should not include it in the generated INSERT statement and later we have to perform additional actions to obtain the ID that has been assigned to our newly added record. When retrieving this newly generated Id value we have to keep in mind that if application runs in multi-user environment there is no guarantee that this will be the maximum ID value in the table. In case we are working with Native Data source type this is currently inserted record in the table. If we are using ODBC data source type to connect to MS SQL database or MS Access database, the way to have this value back is to query the system function @@IDENTITY. Note that for MS Access this feature is available in Access 2000 and later. Next issue that we have to address is the fact that this newly obtained Id has to be written for all child records in crsOrderDetails, so that subsequent TABLEUPDATE() for crsOrderDetails to write correct values. Another very important issue when working with remote data is generating ID values when we need to add more than one record in parent cursor (crsOrders in our case) in same time and then to update all of them. Recommended practice in such cases is to write negative ID values. Why shall we do this? Lets think about a Cursor adapter - powerful tool 2003 Venelina Jordanova 10th European Visual FoxPro DevCon 2003 E-CAD 11

(Group DATA)

multi-user application where two users are entering orders simultaneously. They both start the application and retrieve existing data records. If the last used ID at this time is 2048, both of them will create records starting with 2049 in their cursors. What will happen if the first one entered two orders and then wants to save the data, but meanwhile the second user has already saved one order record? In crsOrders we will have two new records, numbered respectively 2049 and 2050. In scrOrderDetails will be also several records numbered with same OrderID values. After inserting these new records into data source, new ID values will be respectively 2050 and 2051. Therefore our first task afterwards should be to replace OrderId with 2050 in all crsOrderDetails records that till this moment were numbered 2049. Right in this moment we have mixed order details records for both new records, as far as all they contain OrderId value 2050. The only way to avoid this problem is to use ID values that could never come across real used values. Using negative numbers as ID values for newly added records can ensure that these values can never be mixed with IDs generated when data records are inserted into data source. Considering all mentioned above, in AfterInsert method we will write additional code to handle this. For this purpose I added a new property to the caOrders class that will hold the name of corresponding child alias, so the class itself not to require hard coded child cursors name. The code in AfterInsert method that will ensure referential integrity will look like this:
LPARAMETERS cFldState, lForce, cInsertCmd, lResult Local lnOldIDValue, lcFilter, lnNewId If lResult lnNewId = 0 DO case Case Upper(This.DataSourceType) == "NATIVE" * this code relies of fact that when working with * Native data source type, the table remains opened and * record pointer is on newly inserted record lnNewId = Orders.OrderId Case InList(Upper(This.DataSourceType), "ODBC", "ADO") If SQLExec(This.DataSource, ; "Select @@IDENTITY as NewIDValue", "crsNewID") > 0 lnNewId = crsNewID.NewIDValue USE in crsNewID EndIf Otherwise EndCase If lnNewId > 0 lnOldIDValue = Evaluate(Alltrim(This.Alias)+".OrderID") Select (This.cChildAlias) Replace all OrderId with lnNewId for OrderId = lnOldIDValue Select (This.Alias) Replace OrderId with lnNewId Else MessageBox("Error retreiving ID value", 0 + 16, "Error") endif endif

After making these final polishing, our sample application is up and running and its real life begins.

Application growing
After a couple of months flawlessly work, suddenly appears a need to upsize the database to SQL Server. There could be many different reasons: optimizing data processing or by security reasons. So, we need to migrate the data and upgrade our application to access SQL Server database. Thanks to our multi-tier application structure, this renovation will have minimal impact to our code. Fortunately we will need to make changes in data access classes only. Even more we will need to alter only our underlying base class caBseAccess.

Data access reorganization


The most popular approach to access SQL Server data from VFP applications is to use ODBC connections and we will also use it. All we need is to change value of DataSourceType property and to assign a valid ODBC connection handle to DataSource property. Note that you have to manage connection yourself. We can manually handle establishing connection or use the CursorAdapter Builder to help us (see Figure 12). It is always good practice to test your connection to ensure that connection string is properly written and that the connection can be established.
10th

European Visual FoxPro DevCon 2003 12 E-CAD

(Group DATA)

Cursor adapter - powerful tool 2003 Venelina Jordanova

Figure 12. Setting ODBC data source for caBaseAccess class

In fact CursorAdapter Builder does our work and has written necessary code for assigning DataSource value. We can see this code in the Init method of the class.
This.DataSource = sqlstringconnect([DRIVER={SQL SERVER};] + ; [SERVER=JEI-Server;] + ; [Trusted Connection=ON;] + ; [DATABASE=NORTHWIND;])

In case we have designed tables structure considering future data migrating this step will be the only one necessary action to change data access from using native tables toward using ODBC data source.

Table name and field type considerations


For a pity Northwind databases that I use for sample data are not quite identical. OrderDetails table is named slightly different in SQL Servers Northwind database Order Details. Unfortunately this space in its name it dramatic obstacle for caOrderDetails class. We need to open the class and manually to change SelectCmd to the following:
select * from [ORDER DETAILS]

Because fields names and type are same and we do not need to change CursorSchema of the class, but we need also to amend KeyFieldList and UpdatableFieldList properties. Figure 13 shows how this is done using the builder. You can also manually amend the code in the Init method of the class.

Cursor adapter - powerful tool 2003 Venelina Jordanova

(Group DATA)

10th European Visual FoxPro DevCon 2003 E-CAD 13

Figure 13. Setting caOrderDetails class according Northwind database on SQL Server

Migrating data from one type data storage to another must be thoroughly planed. All data types that dont quite match have to be considered. For example the logical data type does not have exactly correspondent data type among SQL Server data types, because Bit data type is presented as N(1) when retrieved into a VFP cursor. In order to have reusable code you can either use additional code to convert different data types or you have to consider using of data types that are identical in the set of data types for different data storages that you intend ever to use.

Accessing heterogeneous data


It often happens in real world that newly developed application needs to use particular data, stored by another existing application. In this case developers are often stuck to the data source of the existing application. This fact limits opportunities to choose most relevant data storage and forces developers to use same data source in order to simplify data access. For example in our sample application we could need to obtain employees data from an existing payroll application. An imaginary example of heterogeneous data can be the following situation: Order entry system developed to use native VFP database, that needs to read employees data from an Access database, keeping in mind that it is planed to migrate payroll application toward SQL Server database. As a developer you have to think how to access Native, ADO and ODBC data source types so that any future data migrations not to cause full application redeveloping. CursorAdapter gives the developers admirable chance to organize data access as independently as possible. Using CursorAdapter classes, data layer can be based on several underlying classes, each one responsible for accessing particular data source (see Figure 14a). Then next level classes, inherited from these base classes are build. Every one specific class holds table specific properties and inherits data source properties. As we already have seen associated cursors are filled according CursorAdapters settings and presentation layer and business logic layer operate with this cursors. When future needs require data migrating you can either simply change data source properties of a particular underlying class or you could just change the parent class of a particular CursorAdapter subclass (Figure 14b).
10th

European Visual FoxPro DevCon 2003 14 E-CAD

(Group DATA)

Cursor adapter - powerful tool 2003 Venelina Jordanova

a) Figure 14. Accessing heterogeneous data

b)

Building a base set of underlying CursorAdapter classes gives you also the flexibility to change data source type programmatically and to develop applications that can access different data source type according customers requirements.

Summary
Being one of the most impressive enhancements in VFP 8, Cursor adapter gives the developers great opportunity to develop flexible reusable data classes. It is vigorous class that helps you easy and rapidly to develop data layer in your applications. Using CursorAdapter classes can significant cut down development resources and efforts when migrating data to different data storage.

Cursor adapter - powerful tool 2003 Venelina Jordanova

(Group DATA)

10th European Visual FoxPro DevCon 2003 E-CAD 15

You might also like