An Introduction to Client

download An Introduction to Client

of 11

description

An Introduction to Client-Server Development in Visual FoxPro

Transcript of An Introduction to Client

An Introduction to Client-Server Development in Visual FoxPro

An Introduction to Client-Server Development in Visual FoxProby Scot Becker

Introduction

Note: This article originally appeared in the Virtual FoxPro Users Group October newsletter and is re-published here with permission of the author and the Virtual FoxPro Users Group.In this article, I will explore the many facets of client-server development with Visual Foxpro. Please note, this article was written under the assumption that not many significant changes or enhancements were made to the Client-Server structure of Visual FoxPro version 5.0. Obviously, this article will leave out many complexities that come with programming on a server. I will try to cover the basics, and leave the reader to further explore this development technique as his/her situation merits. I will use Visual FoxPro version 3.x and Microsofts SQL Server version 6.x for all of my examples. However, most of these examples can be easily transposed to whatever server platform you have.

Benefits of a Data Server versus a File Server

Network Input/Output(I/O):

In a file server architecture, the client requests a portion of a file that is then sent to the workstation for processing. The workstation then has to perform all data selection, processing, and modification to the file before any changes are sent back to the file server for reconciliation. In contrast, a data server acts on a request for data from the data file. The data server then sends the requested data back to the client, which acts like a dumb terminal. If any modifications are made to the data, the client requests an update to only the modified data. This reduces processing time and network I/O.

Client Architecture:

A few years ago, a 486DX66 with 4 to 8 Megabytes of RAM was a high powered workstation. Today, this would be considered a low end workstation. However, those slower machines are still capable of running a well-designed Client-Server application (with additional memory so Visual FoxPro can run more efficiently). How often will your company or client upgrade their machines? Relatively speaking, a Pentium 120 will soon become a Delta 88, but it will still be able to run Client Server applications. Even if you have a network with excellent throughput, a Client-Server application can still benefit by distributing the load so that you can support slower (and additional) client workstations. Another advantage is compatibility with third party products. Not all products, Decision Support Systems in particular, will support or work efficiently with Visual Foxpro. However, most products will support a SQL Server architecture.

Performance:

Query speed will be about equal for both Client-Server and Data-Server applications. However, data entry and processing might fair a little better in a Client-Server application. For example, a company has a customer support department of over 50 users who are constantly viewing and updating customer records one at a time. Performance in this case will almost certainly fair better under a SQL server, which is optimized to handle this kind of overhead.

Data Size:

Another advantage of a data server, a primary advantage for some, is data size. Currently, Visual Foxpro can only support a Table size of 2 Gigabytes. Any more than that, and you will have to break your data structure apart. Microsoft SQL Server will support a table size of up to 8 Terabytes.

Security:

Your data is also far more secure on a data server. DBF file formats are well known, and nothing (besides encryption tools) is to stop someone from also accessing your data from another application. On the other hand, nothing can happen on a SQL Server without a valid user name and password. Furthermore, the database owner has to grant permissions for users to even see his/her tables and can also limit a user to certain columns or rows (via a view). Combining SQL Sever security with the robustness and security features of Windows NT, your sensitive data is quite secure.

Recoverability:

Another advantage of a data server is recoverability. In the event of an unexpected shutdown, the validity of the data file could become corrupt on a file server. On a SQL Server, all transactions are logged. At startup, SQL server checks the transaction log against the data residing in the tables. It will apply any changes that are not reflected in the tables, or it will rollback any uncommitted transactions from the tables.

Other Features:

Lastly, SQL Server will support all the referential integrity and other database perks that Visual FoxPro does (in some cases, better than Visual FoxPro); on-line backup (you can backup your data while users are on the system), and a fuller SQL syntax (outer joins; correlated subqueries, inserts, updates, and deletes; and fuller wild card support). SQL Server is also fully integrated with Windows NT as an NT service.

Utilizing Remote Views

The quickest way to apply your FoxPro application against remote data is to utilize Remote Views. A Remote View is just like a Local View; It is nothing more than a CURSOR that is supplied with data from a SELECT statement. Like Local Views, Remote Views can either be updatable or read only. The only difference between a Remote View and a Local View is that a Remote View is supplied with data from a remote data source.

First, you must set up an Open Database Connectivity (ODBC) connection to your server via the ODBC administrator. ODBC is basically just a translation layer between Visual FoxPros version of SQL and your servers version of SQL (the versions may not always be the same).

You should then setup a connection between Visual FoxPro and your declared ODBC data source. You do not need to use a connection but you will have to supply login ID and password each time you open, modify, or requery the remote view. Creating a connection is easy. From the Data tab, select Connections and then select New. Select your ODBC data source and add your user ID and password. Supply the amount of login information that you wish, but remember, if you do not completely specify the login information, the user will be prompted to enter the missing information. One problem with the Connection Designer is the fact that it does not mask your password. This is only an issue if you do not have a secure development environment and you do not properly distribute your application.

The quickest way (at this point) to create a Remote View would be to use the Remote View Wizard. You can either base your view on the previously defined ODBC data source, or on your connection. Again, if you choose the data source, you will always be prompted for your User ID and Password, but this will not happen if you completely setup your connection. The rest of the Remote view wizard is self-explanatory.

Tip: If you want all of the tables fields in your view, you may want to explicitly choose all of your fields one at a time rather than using the button that selects them all at once. A fast way to do this would be to first select all fields, de-select one field, and then re-select it. The reason for this is that Visual FoxPro will implement the select all button as SELECT * FROM.... rather than SELECT field2, field2,... FROM..... This will be important if, at a later date, you add field to the remote table that you do not want reflected in the view.

Next, open your Remote View in the View Designer and modify any of the selection and filtering criteria (I think you will find that the Wizard is not always adequate in this area). Then select the Update Criteria tab. First, select a key field (the column under the key symbol) for your view. Visual FoxPro will not let you write to your view without specifying a key (also, note that a key can be more than one value). From there, you can select the fields that you want to update (the column under the little pencil); If it is all of the views fields, you can select the Update All button. The Update All button will not automatically select the key to be updatable because, in some situations, you do not want the key to be modified. If you want your key fields to be updatable, you will have to explicitly select them. Then select Send SQL Updates.

Tip: All of these settings can also be specified with the DBSetProp() function.To set up a buffering scheme, issue a SET MULTILOCKS ON, and set the buffering scheme with CursorSetProp(). I would recommend scheme 3, Optimistic Row Buffering, for now, but more on that later. Now open your view, and either BROWSE it or use a form to modify some of the data. All of your changes are held in the buffer and have not been sent back to the remote server yet. To update the remote table, you must issue a TableUpdate() such as:

IF TableUpdate(.T.) .T.

=MessageBox(Update Failed, 0, Error)

=TableRevert()

ENDIF

The first parameter in the TableUpdate() function specifies that all updated rows in the buffer will be sent to the server, and the server will attempt to update them all. If any of the individual updates fails, due to a trigger, constraint failure, etc., the rows that are valid will be updated, and those that are not valid will be reverted to their original values via the TableRevert() function.

The minimum to write a Client-Server application has been completed. However, Performance will be inadequate. The first step towards optimization is the parameterized view. This is implemented by a filtering (WHERE) clause except the expression is evaluated against a memory variable. An example is if you only want to look at a specific record (or record set), do the following:

SELECT..... WHERE CustomerNo = ?lnCustNo

Before you open the view, assign lnCustNo a value. Note: Visual FoxPro will also prompt you for the value of lnCustNo if it is not defined from a previous assignment.

Buffering:

Visual Foxpro supports five styles of table buffering all of which are designated with integer values.

No Buffering (1): This is the default.

Pessimistic Row Buffering (2): In this scheme, the record is locked once it is selected for editing and stays locked until the record pointer is moved or a TableUpdate() is issued.

Optimistic Row Buffering (3): In this scheme, the record is locked only while a TableUpdate() is issued, and stays locked until the record pointer is moved, or until the TableUpdate() is completed.

Pessimistic Table Buffering (4) and Optimistic Table Buffering (5): These are just like their respective row buffering schemes except the entire table is locked until the record pointer is moved or a TableUpdate() is issued.

I would recommend option 3, Optimistic Row Buffering (Also note that Pessimistic locking methods are not supported by Microsoft SQL Server).

Other Useful Functions:

CurVal() and OldVal() will return the values of the buffer and actual table data respectively.

GetFldState() returns the status of a field (appended, modified, deleted, etc.).

GetNextModifed() returns information about modified fields.

CursorGetProp() returns the current property values of the cursor.

AError() returns information about the last error and can be used in building your error handler.

Refresh() refreshes your view with the modified data.

ReQuery() requeries the remote source.

Remote View Tips:

In addition, there are some tweaks to the Remote View that may prove to be of value. You can find these under the SQL WHERE Clause Includes section on Update Criteria tab of the View Designer, and the Advanced Options selection of the Query menu pad (For Example: how many records to fetch at a time, fetching memo fields, and connection sharing). The option of particular interest would be connection sharing. Microsoft SQL Server allocates memory for each connection (about 4 Megabytes for 100 connections). Connection sharing basically sets all views to one connection. As long as you are not doing simultaneous fetches, you will never notice the difference to your application except in increased query execution speed.

Tip: You can use DBGetProp(), DBSetProp(), SQLGetProp(), and SQLSetProp() to view and alter all of these settings.Using SQL Pass-Through

The main difference between Remote Views and SQL Pass-Through functions is that you execute everything using function calls. Your SELECT statement is merely a string parameter. Result sets are returned to CURSORs named SQLResult, SQLResult1, SQLResult2, etc. You will still want to use parameterized queries and all of the tips previously discussed (setting query properties, updatable fields, keys, etc.). The most apparent benefit is that you can execute stored procedures. Your command, for example in ISQL, the query tool that ships with Microsoft SQL Server, EXECUTE(StoredProcedure), is a passed to the server via the SQLExec() function that has the name of the stored procedure in the string parameter.

Another configuration option to mention here is a connection property: Synchronous and Asynchronous Processing. With Synchronous Processing, you have to wait for the query to finish executing before control is returned to Visual FoxPro. This is fine for quickly executing statements, but longer queries where you may want some control of the client during the query execution (for example, to use a thermometer bar) would require Asynchronous Processing.

Tip: You can also use Asynchronous Processing to prompt users during a potential runaway query.Some useful functions that have not been previously mentioned are:

SQLConnect() connects to a server using an ODBC data source, user ID , and password.

SQLStringConnect() connects to a server using an ODBC connection string.

SQLDisconnect() disconnects a connection to the server.

SQLExec() sends a SQL statement to your server for execution.

SQLCancel() cancels an executing statement.

SQLMoreResults() fetches another result set from an executing query.

SQLCommit() sends a commit transaction request to the server.

SQLRollBack() sends a Rollback Transaction request to the server.

SQLTables() returns a list of tables residing in your data source.

SQLColumns() returns a list of columns of the selected table in your data source.

Tip: The SQLTables() and SQLColumns() functions are great for building an interactive query builder.Client-Server Application Design Issues

There are design issues to review when implementing a Client-Server application.

Data Partitioning:

To minimize network traffic, you may want to partition your data. In other words, keep a portion of the data on the local LAN or client machine. Seldom changing data, such as a table of zip codes or states, are particularly good candidates for this, especially if you refer to them often. Tables that change infrequently can be stored locally provided they can be updated automatically or on a scheduled basis. For example, in a Client Server application that I developed, there was a list that changed frequently, but not on a regular basis. The solution for this was to make a scheduled update on a daily basis from the server and to provide a means to temporarily enter a new list item (until the next days scheduled update) if needed. Data items that change, but much more infrequently can be updated by a procedure in a menu command. This will introduce some administrative overhead, but if all your users (or you) have to do is click a menu option once a month (or whenever the data needs to be refreshed). This will minimize overall network I/O.

Multiple Users:

An issue that may not always come up (but it will eventually) is how to handle multiple changes to the same record. For example, two users have the same record buffered and make changes to the buffer. Then, one user completely updates the record before the second user. A regular TableUpdate() will return false for the second user if it sees that the data has changed since the buffer was first written. Which change is right? There are four approaches, which will usually vary on your particular application or data structure. First, we can assume that the current user (who may have the customer on the phone) is always right. You would then issue a forced table update by calling TableUpdate(.T. ,.T.). The second strategy would be to make the user requery the data for comparison before changing it. In this case, a TableUpdate(.T.) will return false, and you will have to issue a TableRevert(). Then, if you have a good error handler, you can prompt the user to try the change again (after they see the other users changes). The third approach would be to issue a TableUpdate(.T.) and update all the records you can, and then notify the user which update failed by utilizing the GetNextModified() function, then issue the TableRevert() function. The last option is to let the user decide which change to implement by using the GetNextModified() function, presenting the user with both options, and letting him/her chose. Then, issue the forced update (TableUpdate(.T. ,.T.)) or TableRevert() as needed.

Distributed Client-Server Architectures:

If you have a distributed (many servers or a WAN) Client-Server architecture, you will need to consider multiple sources of the same data. This can usually be solved on the server side via tools such as replication or scheduled data transfers.

Data Validation:

Generally, you want to keep all data validation rules on the server. This minimizes the burden of version control. To effectively develop this strategy, a robust error handler can trap, report, handle, and interpret ODBC errors. On the other hand, if you apply data partitioning, you will have to implement some sort of local data validation as well, whether it is via code or local table level validation.

Referential Integrity:

Referential Integrity should always be handled by the server (except for local table referential integrity, if any). SQL Server makes this task easy by using of constraints, keys, triggers, and rules; Again, you will need a robust error handler.

Security:

Data security should always be handled by the server. User rights can be as specific or as general as you like through the use of stored procedures, permissions, and views. If you want to secure any local data, you will need to account for this in your application, but all sensitive data should always be kept on the server.

Remote Views vs. SQL Pass-Through

An effective application will make good use of both Remote Views and SQL Pass-Through functions. For data entry, record lookup, and static queries (with a discrete set of parameters), Remote Views are easier to implement and much more intuitive to code and debug. However, for ad hoc querying, reporting, and distributed updates (updating more that one table at a time) you will want to use SQL Pass-Through functions.

Local Data Sets:

Some effective Client-Server applications will also do large data downloads to the client machine on a regular basis, and perform queries against the local snapshot of the server data. This will minimize network traffic, but may not always result in accurate query and report results. If the query does not have to be entirely accurate, consider this option. One large download once a day may be better than many short queries all day. There is a security consideration with local downloads, in that now sensitive data may be stored on the LAN or local machine. Again, all sensitive data should always be stored on the data server.

Prototyping In Visual FoxPro:

Another thing to consider is prototyping your application in Visual FoxPro. This is a good idea for many reasons. First of all, development may occur while your server is being setup. This way, you can adjust your table structure without having to bother with the server until your architecture is more stable. Also, once your table structure is set, you can upsize (only if you have the professional version of Visual FoxPro) your table structure and your data to a Microsoft SQL Server. While prototyping, base your views from the local tables and write your code against the local views just as you would against the remote views. Once you are ready to hit the server, just change your local views to remote views and add in your Pass-Through functions.

Server Administration:

There are many server level issues to consider. These issues are neither a trivial nor always quick to solve. Backup strategies; number of users; memory; storage space; writing of triggers, rules, indexes, and constraints; keys; user and group permissions; and other administrative tasks need to be considered. Also, someone is going to have to oversee the daily maintenance of the server. You will need some good books and/or classes, time, and patience, or find someone who is capable of those administrative tasks.

Conclusion

Visual FoxPros Client-Server capabilities are, in my opinion, second to none. By utilizing the concepts discussed in this article, you can provide added value for your company or client. These skills will always be useful, and should be included in the toolbox of any serious developer. Remember, you do not need a SQL Server to practice these skills or utilize any of the techniques that I have discussed here. Any ODBC source, even Visual FoxPro databases, can act as a remote data source. Also, even if you do not plan on writing any Client-Server applications, you can still use many of the techniques discussed in this article in any multiple user environment.

The Author would like to thank (in alphabetical order) Mike Bisek, Chad Coon, and Leah Quam for their assistance in formulating this document. This document is Copyright, 1996 by Scot Becker and may not be re-printed or re-published in any format without the permission of the author and the Virtual FoxPro Users Group

This page was last modifed: undefined (this is not always available on all browsers)

[email protected]