Вы находитесь на странице: 1из 36

Getting Started with dbExpress

by
Martin Rudy
One of the many data connectivity options for the Borland RAD products is dbExpress.
This set of drivers and components provide connectivity to databases for the
Windows .!ET and "inux platforms. This paper covers an introduction to usin# the
dbExpress technolo#y in both Win$% and .!ET.
!OTE& !ot all of the material in this paper will be covered in the 'onference session.
The intent is to provide more detail in this paper that can be referenced durin# the
session.
Contents
The ma(or topics covered are&
Data 'onnectivity Overview
dbExpress )ntroduction
Basic Data 'onnectivity
*endin# +pdates to a Database , The Basics
Reconcilin# Errors
-aster . Detail , !estedData*ets
*ettin# +pdate-ode and /rovider0la#s properties
Data*et/rovider /roperties and Events
Empty 0ield "ist for +pdate *tatement
*ummary
Data Connectivity Overview
'ontents
Borland RAD tools provide connectivity to a variety of data file formats. 1ou can
connect to virtually any type of data if a driver is available for the data format.
dbExpress is a set of li#htwei#ht *2" drivers for )nterBase -icrosoft *2" *erver
-1*2" Oracle *2" AnyWhere and DB%.
A basic understandin# of the foundational concepts of data connectivity is essential
before efficiently usin# many of the database features. These concepts include the
definition of terminolo#y and the basic steps to display data in forms. To view and
modify data you need to define the source of the data you intend to use how to display
the data at runtime and how to connect the two. This is done usin# a Data*et
Data*ource and data3aware controls.
Figure 1: Basic requirements to display data in a form
0i#ure 4 shows the dependencies between a form and the data displayed in the form. The
data files are defined usin# a Data*et component. The data can be in a table the result of
a 5uery or defined by a stored procedure in a *2" database server. The form contains
data3aware components that display the data. A Data*ource is the component used to
connect data3aware components to Data*et components.
db!press " #ntroduction
'ontents
dbExpress is a cross3platform database3independent set of drivers and components that
provide hi#h3performance and a small footprint. )t consists of database connectivity
technolo#ies introduced in Delphi 6 and 7ylix and is one of the data connectivity options
in Delphi %889 for both Win$% and .!ET applications. 0or .!ET applications it is called
dbExpress.!ET which is a .!ET version of the same capabilities found in Delphi :
dbExpress.
'onnectin# to data re5uires the database to be identified the data table;s< and columns
selected and a re5uest for the defined data. The ma(or difference between dbExpress and
the other RAD data connectivity options is dbExpress only supports unidirectional read3
only cursors for data retrieval. This means no data is buffered and some of the features
available in the other data connectivity options are not applicable. The followin# are
some of the features not available with unidirectional cursors&
0orward movement only cannot use "ast and /rior methods
!o editin# of data
!o filter support
!o batch moves
!o hetero#eneous 5ueries
!o cached updates
!o loo=up fields
At first #lance you mi#ht thin= dbExpress is not for you but wait it is really a more
efficient way to retrieve and update data. The techni5ue used by dbExpress to manipulate
data is the same as buildin# multi3tier applications usin# Data*nap ;formerly =nown as
-)DA*< , you use 'lientData*ets. The li#ht3wei#ht fast dbExpress drivers 5uic=ly
retrieve data from the database and 'lientData*ets provide the ability to modify data
chan#e sort orders and maintain a chan#e lo# support loo=ups filterin# plus much
more. +sin# dbExpress as it is desi#ned re5uires you to perform a fetch of only the data
that users need to immediately use not the entire table. !o lon#er will a *E"E'T >
0RO- tablename be sufficient ;unless the result set is not too lar#e<. 'lientData*ets are
in3memory tables all data retrieved as the result set is stored in the local wor=station?s
memory. This basic architecture is called provide.resolve.
)n Win$% two ini files are used to store information about drivers and database
connections. The installed driver types are contained in db!drivers. 0or each driver
installed the re5uired libraries ;D""s for Windows and shared ob(ects in "inux< are
listed alon# with default connection parameter settin#s. The second ini file used
db!connections contains the named confi#uration connection sets.
The components available in dbExpress and dbExpress.!ET are essentially the same.
0i#ures % and $ show the 'omponent.Tool /alettes available in Delphi : and Delphi
%889. Table 4 lists the components on the dbExpress component palette pa#e and a brief
description.

Figure $: db!press components in Delp%i &
Figure ': db!press components in Delp%i $(()
*able 1 " db!press components
Component +ame Description
*2"'onnection Defines a connection to a database similar to TDatabase
*2"Data*et @eneral3purpose unidirectional dataset that executes the *2"
statement defined by the 'ommandText property. This can
be a *E"E'T statement that returns a dataset a *2"
statement that does not return data or executes a stored
procedure.
*2"2uery *upports *2" statements to be executed that return a
unidirectional result set or update data or database schemas.
*2"*tored/roc Executes a stored procedure. )f there is a result set it is
unidirectional.
*2"Table /rovides unidirectional access to a database table.
*2"-onitor +sed to intercept and display messa#es passed between a
*2"'onnection and database.
*imple'lientData*et 'ombines a *2"Data*et and Data*et/rovider internally in
the component to support data cached in memory.
The *2"'onnection is where the database connectivity is defined. One of the next four
components in the list is used to specify the data to be retrieved if you intend to use a
unidirectional cursor. )f data is to be edited and browsed the *imple'lientData*et needs
to be used or a 'lientData*et component which is covered later. )n the next section the
basics of retrievin# data usin# dbExpress is covered.
Basic Data Connectivity
'ontents
With dbExpress you start by usin# a *2"'onnection component. The first step is to
define the database connection. This can be done usin# an existin# connection definition
creatin# a new connection definition or usin# the /arams property of the *2"'onnection
to define a connection dynamically. We will start usin# an existin# definition.
The Connection+ame property is assi#ned the name of an existin# definition.
'onnection definitions are stored in an ini file named db!connections. This file stores
the connection confi#urations settin#s and specifies which dbExpress driver to use. The
available drivers are maintained in an ini file named drivers. This file contains the D""
or *O name re5uired for connection and the default settin#s for all the connection
parameters. *ettin# the 'onnection!ame property can be done by selectin# an entry in
the drop3down or double3clic=in# on the *2"'onnection component which displays the
dbExpress 'onnection Editor. This editor is essentially the same in both Delphi : and A
as shown in 0i#ures B and 9.
Figure ,: db!press Connection ditor dialog " Delp%i &
Figure ): db!press Connection ditor dialog " Delp%i $(()
)n this dialo# you select the database drive connection name and modify properties used
to connect to the database. The Driver !ame combo box specifies which connections are
displayed in the 'onnection !ame list box. By default all defined connections are listed.
'han#in# Driver !ame value reduces the 'onnection !ame listin# to only those for the
selected driver. The 'onnection *ettin#s vary dependin# on the driver selected. Any of
the entries in the Calue column can be modified. 1ou cannot enter new 7ey values or
delete any of the existin# ones.
-electing component to retrieve data
There are four main unidirectional dataset components on the dbExpress component
palette& *2"Data*et *2"2uery *2"*tored/roc and *2"Table. The last three are
similar to the BDE ADO and )nterBase Express components. The *2"Data*et
component is a #eneral3purpose component and is the recommended component to use.
All four components have a *2"'onnection property that lin=s to a *2"'onnection
component.
The *2" statement to execute for a *2"Data*et is defined in the Command*e!t
property. 1ou can enter or paste an *2" statement into the property or clic= the ellipses
button to display a 'ommandText Editor dialo# to select from the list of tables and fields.
0i#ure 6 shows a completed dialo# selectin# all rows and columns from the '+*TO-ER
table.
Figure .: Command*e!t ditor in Delp%i & and Delp%i $(()
One of easiest components to use for displayin# data is the DB@rid. )t is located on the
Data 'ontrols tab. )f you attempt to connect a Data*ource and a DB@rid to a
unidirectional dataset you #et an exception raised with the text Operation not allowed
on unidirectional dataset. A #rid re5uires the ability to scroll forwards and bac=wards
throu#h the dataset which cannot be done with a unidirectional dataset. )f you connect
other data3aware components ;e.#. DBEdit< the exception is not raised until you attempt
to #o to the prior or last record in the dataset.
/sing t%e -impleClientData-et component in Delp%i
The *imple'lientData*et component is a sin#le component to use for bi3directional
movement throu#h a result set. This component uses an internal *2"Data*et and
TData*et/rovider for retrievin# data and also supports data modification. The
Data*et/rovider creates datapac=ets based on the *2" statement defined by the
'ommandText property and used by the *2"Data*et. The datapac=ets are cached in
memory. When edits are sent bac= to the database the Data*et/rovider processes
datapac=ets containin# (ust the modified recordsD it creates the appropriate *2"
statements that are sent to the database. Any error records returned from the database are
pac=a#ed by the Data*et/rovider and made available for review in the
*imple'lientData*et. This provide.resolve cycle continues until the records are accepted
by the database or the edits are canceled.
The *imple'lientData*et re5uires the same two basic pieces of information as the
*2"Data*et , which database connection and the *2" statement. The Connection
property is used to identify the *2"'onnection to use. Alternatively you can use the
Connection+ame property to select from the connection list. This will create an internal
*2"'onnection component. The *2" statement is defined in the same manner as the
*2"Data*et component , usin# the 'ommandText property. 0i#ure : shows a basic
setup in desi#n mode.
Figure &: Basic db!press app in design mode
Retrievin# data is done when the Active property of the *imple'lientData*et is set to
True. This can be done at desi#n time or when the form is created. )n addition to issuin#
-impleClientData-et01ctive :2 *rue the Open method ;-impleClientData-et0Open<
can be used on the *imple'lientData*et which is the e5uivalent of settin# the Active
property to True. When Active is set to True if the *2"'onnection has not opened
access to the database it will initiate a connection attempt to set its 'onnected property
to True. The *2"'onnection component also has a 3ogin4rompt property. By default
this property is True indicatin# a lo#in dialo# will be displayed where a user name and
password can be entered before attemptin# to connect to the database. 1ou can also
include those values in the connection parameters and remove the lo#in dialo# by settin#
the "o#in/rompt property to 0alse.
3imitations of -impleClientData-et
The *imple'lientData*et is #reat for 5uic= demos and basic data retrieval. Beyond these
two uses there are limitations that may prevent you from fully usin# the power of
dbExpress. /roperties and events of the internal components are not surfaced. This
prevents you from creatin# multi3tier applications create nested datasets utiliEe any of
the Data*et/rovider features and if an internal *2"'onnection is used it cannot be
shared with other datasets.
A more appropriate approach is to use a 'lientData*et ;'D*< and Data*et/rovider.
Connecting to data using a CD- and D-4
Both the 'D* and D*/ components are located on the Data Access component palette.
A *2"Data*et component from the dbExpress palette is used to define the result set.
The *2"'onnection property of the *2"Data*et is assi#ned to the *2"'onnection
component name. The *2"Data*et 'ommandText property defines the *2" statement.
The Data*et property of the D*/ is set to the name of the *2"Data*et component. The
'D* component has a /rovider!ame property which is set to the D*/ name. The last
property value chan#e is to the Data*ource Data*et property which should be assi#ned to
the 'D* component name. *ettin# the 'D* Active property to True ;or usin# the Open
method< retrieves the data.
At first #lance you mi#ht feel this is too much wor= compared to the
*imple'lientData*et. As we cover more of the 'D* and D*/ features you #ain a better
understandin# of why you will use a D*/ and 'D* instead of the *imple'lientData*et.
-ending /pdates to a Database " *%e Basics
'ontents
Runnin# the application shows you can move throu#h the data insert edit and delete
data. When you close the application no data is saved. 1ou need to explicitly save the
data usin# the Apply+pdates method. This method sends to the provider all inserted
deleted and modified records in the chan#e lo# from the 'D*. Apply+pdates is a
function that ta=es a sin#le parameter and returns an inte#er value.
The Apply+pdates parameter indicates the maximum number of errors allowed before
the update process is canceled. A value of 34 indicates any number of errors can occur.
Any record that could not be posted is returned to the client in the data pac=et. When Eero
is used the first record that #enerates an error causes the entire update process to be
aborted. !o records are removed from the chan#e lo#D they are all retained by the 'D*.
Also no chan#es are written to the database. Any number #reater than Eero used for the
parameter indicates the maximum number of errors that can occur before the entire
update process is aborted. )f there are less error records than the value of the parameter
all records that were successfully saved are committed and the error records are still
retained by the 'D*.
The value returned by Apply+pdates indicates the number of records that could not be
posted. This allows you to chec= the result of an Apply+pdates statement to conditionally
proceed based on the existence or non3existence of any errors.
When Apply+pdates results in records bein# successfully saved the Data property is
automatically updated. Any error records from the update alon# with the newly updated
records are placed in the updated Data property.
Below is a simple example of how a button can be used to send updates to the database.
The value returned by Apply+pdates is saved in the variable iErr'nt. *ince 34 is used as
the parameter there is no limit on the number of errors. All records that can be posted
will be committed and all error records will remain in the Data property of the 'D*. )f
the value is F 8 then a messa#e is displayed alon# with the number of remainin# errors.
procedure TfrmMain.pbUpdateClick(Sender: TObject);
var
iErrCnt: Integer;
begin
iErrCnt := cdC!t"mer.#ppl$Update(%&);
if iErrCnt ' ( then
Meage)lg(*+r"blem in appl$ !pdate, * - IntT"Str(iErrCnt) -
* err"r().*,mt.arning,/mbO01,();
end;
/ndoing c%anges
'han#es can be reversed in a 'D* to a variety of levels. They include an incremental
undo a record undo all chan#es and refreshin# the data currently in the provider.
)ncremental undo is accomplished usin# the +ndo"ast'han#e method. This method
reverts an entire record to it previous set of values. !ote you cannot undo to the field3
level only the record3level. Each call to the method reverts in se5uence the previous
chan#e to the dataset. There is one parameter used with the +ndo"ast'han#e method that
is a Boolean data type. When the value is True the cursor is repositioned on the record
that is restored. The cursor remains in its current position when the parameter is 0alse.
Below is an example of +ndo"ast'han#e where the cursor follows the chan#e.
cdC!t"mer.Und"2atC3ange(Tr!e);
'allin# RevertRecord can reverse all chan#es for the current record. This method
removes all chan#es for the current record from the chan#e lo#. Below is an example for
RevertRecord.
cdC!t"mer.4e5ert4ec"rd;
All chan#es can be canceled usin# 'ancel+pdates. When this method is called every
entry in the chan#e lo# is removed. Before callin# 'ancel+pdates the 'han#e'ount
property can be used to determine if there are any chan#es to the dataset. The followin# is
an example of usin# this property with 'ancel+pdates.
if cdC!t"mer.C3angeC"!nt ' ( then
begin
if Meage)lg(*#re $"! !re $"! 6ant t" cancel all c3ange7*,
mtC"nfirmati"n,/mb8e,mb9"1,() = mr8e then
begin
cdC!t"mer.CancelUpdate;
cdC!t"mer.4efre3;
end;
end
else
Meage)lg(*T3ere are n" c3ange t" !nd".*,mtInf"rmati"n,/mbO01,();
Refres%ing client data
Two methods can be used to refresh the data without closin# the dataset& Refresh and
RefreshRecord. Refresh returns the current values from the server. An exception is raised
if there are any records in the chan#e lo#. )t is best to chec= for any chan#es in the lo#
usin# 'han#e'ount first then either cancels all chan#es or prompt users to o=ay clearin#
the chan#e lo#. RefreshRecord updates the current record to those currently in the
provider. RefreshRecord also carries a warnin# when there have been updates to the
record. +pdate conflicts can exist which means no reconcile error will occur even when a
conflict exists. Below is an example of usin# the Refresh method.
if cdC!t"mer.C3angeC"!nt ' ( then
begin
if Meage)lg(*C!rrent c3ange l"g 3a n"t been applied. )" * -
* $"! 6ant t" cancel all c3ange7*,mtC"nfirmati"n,
/mb8e,mb9"1,() = mr8e then
begin
dmMain.cdC!t"mer.CancelUpdate;
dmMain.cdC!t"mer.4efre3;
end
else
pbUpdate.Set:"c!;
end
else
dmMain.cdC!t"mer.4efre3;
)n this example a preemptive test is done before callin# Refresh. )f there are any chan#es
in the lo# the user is prompted to cancel all chan#es before refreshin# the data.
3imiting record display based on update status
Each record that is modified inserted and deleted #ets mar=ed with a status value. The
*tatus0ilter property can be set to limit the types of records displayed. *tatus0ilter can be
set to one of more of the values in Table %.
*able $ " -tatusFilter options and descriptions
5alue Description
us+nmodified Record has not been modified.
us)nserted )nclude records that have been inserted.
us-odified
"imit records displayed to only those that have been modified. Two
records are displayed for each ori#inal record modified& the ori#inal
record alon# with a second record containin# all modifications for each
postin#.
usDeleted *how records that have been deleted.
*tatus0ilter can be an empty set one of the values or multiple values. When *tatus0ilter
is an empty set all records are displayed. )f more than one value is used the combination
of the options included in the set determines which records are displayed.
Reconciling rrors " /sing t%e Built6in Dialog
'ontents
Another aspect of data entry is correctin# records that cannot be saved. There are many
reasons for error records. Application validation business rules database constraints and
data modified by another user are some of the main reasons. Reconcilin# these types of
errors can be handled in the application the database or a combination of both. This next
section introduces the basics of a built3in feature to provide error reconciliation in the
application.
The Ob(ect Repository contains a dialo# box that provides the basic reconciliation code
already completed. 0i#ure A shows the entry on the Dialo#s tab in the Ob(ect Repository
for Delphi :. The Ob(ect Repository for Delphi %889 is shown in 0i#ure G.
Figure 7: Reconcile rror Dialog option in t%e Ob8ect Repository for Delp%i &
Figure 9: Reconcile rror Dialog option in t%e Ob8ect Repository for Delp%i $(()
0+* pro8ects
Addin# the Reconcile Error Dialo# to a pro(ect re5uires you to do three thin#s. 0irst the
unit for the dialo# must be removed from the list of auto3created forms for the pro(ect
which is done automatically for you when you add the dialo# to a pro(ect unless you have
turned off the auto create feature. *econd you must modify the OnReconcileError event
of the 'lientData*et component. Below is an example of the minimum code for the
event. The last step is to add the unit for the dialo# to the form or data module where the
'D* component resides. )n the sample code the uses clause in the data module is
updated.
procedure TdmMain.cdC!t"mer4ec"ncileErr"r()ataSet:
TC!t"mClient)ataSet;
E: E4ec"ncileErr"r; Update0ind: TUpdate0ind;
var #cti"n: T4ec"ncile#cti"n);
begin
#cti"n := ;andle4ec"ncileErr"r()ataSet, Update0ind, E);
end;
The Reconcile Error Dialo# is displayed after a call to Apply+pdates when one or more
error records exist. Each record that cannot be posted is displayed in the dialo# box one
error at a time. 0i#ure 48 shows an example when there is an attempt to save a record and
another user has already made a modification to the record.
Figure 1(: Reconcile rror Dialog at runtime
The dialo# shows errors for records you have inserted modified and attempted to delete.
The +pdate Type label indicates which of the three actions you too= on the record. The
#rid has a maximum of three columns. They are the value you are attemptin# to save
;-odified Calue< the value of the field as it is currently in the database ;'onflictin#
Calue< and the value when the client application ori#inally received the data pac=et
;Ori#inal Calue<.
The #rid chan#es display based on the type of problem the update action and which of
the two chec= boxes at the bottom of the dialo# are chec=ed. The first chec=box Show
conflicting fields only limits the records in the #rid to only those fields that have been
chan#ed and are in conflict with current database values. 0i#ure 48 shows an example of
this selection. )f the Show changed fields only chec=box is chec=ed the fields in all
records that have been chan#ed are listed. )f there is no conflictin# value the 'onflictin#
Calue column contains H+nchan#edF. )f you attempt to delete a record that has already
been deleted by another user only the -odified Calue column is displayed. The radio
buttons in the upper ri#ht provide the type of action you can with the error record. Table
$ contains a description that is directly from the Delphi Ielp system.
*able ' " 1ction options in t%e Reconcile rror Dialog
5alue Description
*=ip
*=ips updatin# the record that raised the error condition and leaves the
unapplied chan#es in the chan#e lo#.
Abort Aborts the entire reconcile operation.
-er#e -er#es the updated record with the record on the server.
'orrect
Replaces the current updated record with the value of the record in the event
handler.
'ancel Bac=s out all chan#es for this record revertin# to the ori#inal field values.
Refresh
Bac=s out all chan#es for this record replacin# it with the current values
from the server.
The action for each error can be set before clic=in# the O7 button. Based on the type of
error you can determine to leave the record in the chan#e lo# cancel it mer#e the
updated record or abort the entire reconcile operation.
Master : Detail 6 +estedData-ets
'ontents
There are two options for creatin# master.detail forms usin# 'lientData*ets. 1ou can use
the option of lin=in# the results of two datasets after the data is retrieved usin# the
-aster*ource and -aster0ields properties or define the master detail relationship with
*2"Data*ets and use the feature called nested datasets. The second option is what will
be described next.
The ma(or difference with nested datasets over usin# the -aster*ource and -aster0ields
properties is a sin#le D*/ component encapsulates both the master and detail data. The
detail data is actually a column within the master dataset. Another feature of nested
datasets is all updates are wrapped in a sin#le transaction. 0i#ure 44 shows the form in
desi#n mode.
Figure 11: +ested dataset master : detail setup in design mode
The first step to setup up master . detail relationship is to select two unidirectional dataset
components. )n this example two *2"Data*et components are used to define which
data to retrieve. A Data*ource component is also needed to lin= the two datasets. The
first *2"Data*et ;named sds'ustomer< retrieves all the records from the 'ustomer table
usin# select ; from Customer as the *2" statement. The Data*ource component
;named ds'ust< uses the *2"Data*et for the 'ustomer data as its Data*et.
The second dataset ;named sds*ales< is used for the detail data from the *ales table. To
lin= the two *2"Data*et components the DataSource property of the *ales *2"Data*et
component is used alon# with the WIERE clause of the *2" statement. The Data*ource
property is set to ds'ust the name of the Data*ource component for the 'ustomer table.
This defines the dataset to use as the master data. The lin=a#e between the two tables is
defined in the *2" property usin# the WIERE clause as follows&
-3C* ; FROM Orders
<=R C/-*>+O 2 :cust>no
)n this example all of the columns from the *ales table are selected. The rows are limited
to the value in the custJno parameter. The =ey to usin# this techni5ue is the parameter
names must match the names of the lin=in# fields in the master table. )n this example
'+*TJ!O is the field name in both tables. Therefore &custJno is used as the parameter
in the WIERE clause. "owercase characters are used to visually seein# a difference
between the field name and parameter name but they are not re5uired. The value of
&custJno is automatically updated each time the master record chan#es which in turn
#enerates a new *2" statement and the appropriate detail data is returned.
Ciewin# the data with 'lientData*ets only re5uires a sin#le TData*et/rovider. )t is
lin=ed to the *2"Data*et for the master table. !o provider is re5uired for the detail data.
The data for the *ales table will automatically be made available by the nestin# of the
detail data as a separate field for each master record.
The ma(or difference in settin# up master . detail usin# nested datasets is in settin# the
'lientData*et properties. Two 'D* components are re5uired. The first 'lientData*et
used as the master table is lin=ed directly to the provider. An additional step is re5uired
to create T0ield descendants for the fields in the master dataset. When this is done a new
field is listed that does not exist in the 'ustomer table. 0i#ure 4% shows the T0ields
Editor for the 'ustomer 'D*. The additional field contains the detail records for the
*ales table. )t is a TData*et0ield descendant. The name of this field is the same as the
name of the *2"Data*et.
Figure 1$: Fields ditor dialog bo!
A second 'D* is used for the detail data. )nstead of lin=in# the 'D* used for the detail
data to a provider the DataSetField property of the 'lientData*et is used. This property
is set to the TData*et0ield instantiated in the master data 0ields Editor. When both 'D*
components are opened the data for the 'ustomer and *ales are displayed. 0i#ure 4$
shows an example of usin# nested datasets in a DB@rid.
Figure 1': 5isual display for nested datasets
)n the DB@rid the column for the detail dataset is displayed similar to memo and #raphic
data. The text ?D1*1-*@ represents the values for each record in the dataset. An
ellipsis button is displayed with a double3clic= or 0% =eypress. 'lic=in# the button
displays another window containin# the detail data displayed in a #rid. This window will
always stay on top. 1ou can also see in 0i#ure 4$ that the bottom #rid in the form
displays the same data. 1ou are not re5uired to include the Data*et0ield in the master
table. The T0ield Cisible property can be set to 0alse and usin# a second 'D* and
Data*ource all detail can be displayed in a #rid.
Another difference when 'lientData*ets are used instead of *2"'lientData*ets is in
savin# the data. The *ave 'ustomer button on the form is the same as in example
pro(ects usin# *2"'lientData*ets. When chan#es are made here and the Apply+pdates
is executed all modifications for both the master and detail data sets are wrapped into a
sin#le transaction and committed or rolled bac= based on the parameter passed in the
Apply+pdates method.
-etting /pdateMode and 4roviderFlags properties
'ontents
'ontrol of the type of optimistic loc=in# that is used in an application is established usin#
the dataset +pdate-ode property. The +pdate-ode settin# dictates the fields in the
dataset used to find the ori#inal record. *pecifically this property sets what fields are
used in the WIERE clause of the +/DATE and DE"ETE statement #enerated by the
provider. )f the record is not found with the same values as when the record was
ori#inally read the update fails and an exception is raised. Where you set this property is
based on which components you use. +sin# 'lientData*ets the +pdate-ode property is
on the Data*et/rovider. )f you use one of the combination 'D*.D*/ components ;e.#.
*imple'lientDataset< those components have an +pdate-ode property because of the
internal Data*et/rovider. Table B lists the three values for +pdate-ode and their
description.
*able , " /pdateMode property settings
5alue Description
upWhereAll Every column in the record is used to find the record. This is the Delphi
default.
upWhere'han#ed Only the columns in the record bein# edited that have chan#ed are used
to find the record.
upWhere7eyOnly Only the columns that define the =ey are used to find the record.
'areful consideration should be #iven to the value selected for the +pdate-ode. Each
settin# has its #ood and bad side. The default upWhereAll is the most restrictive and has
the worst performance. +sin# all the fields in the WIERE clause to locate the ori#inal
record ensures the record has not been chan#ed since it was ori#inally read. )f you #et an
error messa#e statin# the 5uery is too complex when you attempt an update the *2"
database is indicatin# it cannot handle a WIERE clause with all the fields in the table
bein# used. The upWhere7eyOnly is the least restrictive. This allows anyone to chan#e
any field ;except the primary =ey< without consideration of what the ori#inal values
where. )n between these two property options is the upWhere'han#ed. This can be a
problem where the values in multiple fields ta=en to#ether have a specific meanin# li=e
in a multi3field primary =ey where users are allowed to chan#e the values. Iavin# a #ood
database desi#n and a well thou#ht out set of business rules assist in determinin# the
correct +pdate-ode settin#.
Testin# this feature can be done by chan#in# the +pdate-ode property and runnin# two
instances of an application ma=in# modifications in each.
The /rovider0la#s properties for T0ield components are also available to use in
determinin# how an update is to be processed. Table 9 lists the possible options.
*able , " /pdateMode property settings
5alue Description
pf)n+pdate 0ield is included in update
pf)nWhere 0ield is used in findin# the ori#inal record to be updated
pf)n7ey 0ield is used in findin# the current record after an update fails
pfIidden 0ield is included in the data pac=et to ensure uni5ueness of the record.
The field is used to find the ori#inal record to update. The field is not
visible to the application.
Each field in the dataset used by the provider can set which of the /rovider0la#s are
applicable. 'han#in# the /rovider0la#s modifies the *2" +/DATE and DE"ETE
statements created by the provider to chan#e and remove records from the database.
To demonstrate the impact on updatin# data and the usa#e of the +pdate-ode and
/rovider0la#s properties the followin# *2" statement will be used with the )nterBase
Employee database. This example is in the /rovider0la#s pro(ects which is usin#
dbExpress.
SELECT CUST_NO, CUSTOMER, CONTCT_!"RST,
CONTCT_LST, #$ONE_NO,
%%RESS_L"NE&, %%RESS_L"NE', C"T(,
STTE_#RO)"NCE, COUNTR(,
#OSTL_CO%E, ON_$OL%,
**C"T( ++ ,,,- ++ STTE_#RO)"NCE- S C"T(STTE
!ROM CUSTOMER
All of the fields from the 'ustomer table are returned and a calculated field combinin#
the 'ity and *tateJ/rovince field which is named 'ity*tate.
Any attempt to modify an existin# record or delete a record #enerates an error with the
messa#e K'olumn un=nown ')T1*TATEL. The problem is in the WIERE clause that is
#enerated. Below is the *2" #enerated for deletin# a record. The dbExpress
*2"-onitor is used in the pro(ect to trac= *2" statements.
delete fro. CUSTOMER
/here
CUST_NO 0 1 and
CUSTOMER 0 1 and
CONTCT_!"RST 0 1 and
CONTCT_LST 0 1 and
#$ONE_NO 0 1 and
%%RESS_L"NE& 0 1 and
%%RESS_L"NE' is null and
C"T( 0 1 and
STTE_#RO)"NCE 0 1 and
COUNTR( 0 1 and
#OSTL_CO%E 0 1 and
ON_$OL% 0 1 and
C"T(STTE 0 1
The WIERE clause includes all fields listed in the *E"E'T statement includin#
')T1*TATE. This DE"ETE will always fail because of the calculated field
')T1*TATE is not a field in the base 'ustomer table.
'han#in# the +pdate-ode property from the default of upWhereAll to upWhere'han#ed
or upWhereAll prevents this type of error dependin# on the type of modification
performed. Either option will eliminate the column un=nown error when modifyin# an
existin# record. Below is an example of the *2" #enerated to update the 'ontactJ0irst
field and +pdate-ode is set to upWhere'han#ed. The WIERE clause includes the
primary =ey ;'ustJ!o< and all fields chan#ed in this example (ust 'ontactJ0irst.
update CUSTOMER set
CONTCT_!"RST 0 1
/here
CUST_NO 0 1 and
CONTCT_!"RST 0 1
+sin# upWhere'han#ed when attemptin# to delete a record still causes the error. This is
due to the inclusion of all fields in the WIERE clause. To #et a delete to wor= you need
to set the +pdate-ode to upWhere7eyOnly. With this settin# only the 'ustJ!o field
will be used in the WIERE clause as shown below.
delete fro. CUSTOMER
/here
CUST_NO 0 1
An alternative to solvin# the 'ity*tate un=nown column problem is to use the T0ield
/rovider0la#s. The dataset used by the provider are the T0ields that need to be chan#ed.
*ettin# all the /rovider0la#s to 0alse for 'ity*tate prevents its usa#e in the WIERE
clause for any *2" statement. An +/DATE statement where the 'ontactJ0irst is
modified now is as follows&
update CUSTOMER set
CONTCT_!"RST 0 1
/here
CUST_NO 0 1 and
CUSTOMER 0 1 and
CONTCT_!"RST 0 1 and
CONTCT_LST 0 1 and
#$ONE_NO is null and
%%RESS_L"NE& 0 1 and
%%RESS_L"NE' is null and
C"T( 0 1 and
STTE_#RO)"NCE 0 1 and
COUNTR( 0 1 and
#OSTL_CO%E 0 1 and
ON_$OL% is null
The /rovider0la#s can also be used to reduce the field used in the WIERE clause even
to the point of specifyin# (ust the primary =ey.
Data-et4rovider 4roperties and vents
'ontents
The TData*et/rovider ;D*/< is the conduit between the database and the client. )t
connects to a dataset component that provides the data from the database. The
Data*et/rovider is responsible for pac=a#in# the data pac=ets and sendin# them to the
client application when a re5uest is received. )t also receives the updates from the client
and attempts to post the data. Any error record that is #enerated is sent bac= to the client
application.
The expectation of those attendin# the session for this paper have a basic understandin#
of how to use the Data*et/rovider with a TData*et descendant and a T'lientData*et.
The remainder of the introduction covers topics that are (ust beyond the basics of how
connect the D*/ in an application.
=ow and <%ere to Define Constraints
)n a multi3tier application there are many places where constraints can be placed , client
database server or the application server. )n a two3tier application it has to be at the
client or at the server. Each level has its advanta#es and disadvanta#es.
)n you put constraints in the client application you #et instant notification of data
problems without server in5uiries and you have decentraliEed the business rules. This
means the validation chec=s are pro#rammed in each application. )f you have more than
one application accessin# the data the constraints have to be replicated in each client
application. This becomes a maintenance ni#htmare.
-ost database servers provide some level of constraint definition. This varies from
schema definition level to pro#rammin# domains rules tri##ers and stored procedures
that are called to further test data modifications. 'onstraints defined in the database
provide the most centraliEed set of rules tied directly to the data. !o matter how data is
modified these rules are applied. There are two issues that result in the possibility of
au#mentin# server3based rules& cryptic error messa#es from the servers displayed to users
and sometimes business rule lo#ic is more sophisticated than the server pro#rammin#
lan#ua#e can support.
)n a multi3tier application you have a third option which is to pro#ram constraints in the
application server. Iere you can use the power of Data*nap to implement sophisticated
lo#ic for business rules. 1ou can ta=e the errors #enerated by database server constraints
and chan#e the messa#es sent bac= to the client you and also add constraint routines to
further validate data sent from the client before committin# it to the database server. )n a
two3tier application this same techni5ue can be used but the code resides in the client
application.
)n reality you will use constraints at all three levels. *ome of the database3server level
constraints can be passed directly to client applications from the server. 1ou can also
have application server constraints available in the client application each time a
connection is made. This provides the best of all three levels.
0or this section the E-/"O1EE.@DB )nterBase database that ships with Delphi is used
as the data source. -ost of the examples use the *A"E* table. The server and client
pro(ects are found in the BusRules'nstrnts directory of the code examples.
*Field properties automatically applied
*ome of the T0ield properties are automatically passed to the client from the application
server. -ost of the property values are defined at desi#n time with one of the properties
defined based on the type of TData*et control used and the table?s schema definition. To
use the automatic T0ield property feature you must create persistent T0ield ob(ects on
the TData*et component. )n this example a 5uery control for the *A"E* table has
persistent T0ields with some of the properties modified as described in Table 9.
*able ) " *Field properties automatically sent to client application
4roperty Description
ReadOnly )ndicates the field cannot be modified
Re5uired Calue must be placed in the field before the record can be posted
DefaultExpression
Default value for the field for any inserted records
Example& M*an Die#o?
'ustom'onstraint
Defines a custom field3level constraint usin# *2" Where clause type
expressions. Any validation rule added here au#ments the server3level
validation rules.
Example& x F 48 and x H 4888
'onstraintError-essa#e
The messa#e that is displayed on the client machine when a value does not
meet the custom constraint.
Example& Calue must be between 48 and 4888
All of these T0ield properties are available in sin#le tier two3tier and multi3tier
applications there is nothin# built into Data*nap to utiliEe this capability. This set of
T0ield properties reduces the amount of codin# re5uired to support additional constraints
not supplied by the database server and allow for ease in settin# custom error messa#es.
Error messa#es displayed based on violation of the properties differ based on the data
type and what property is set. The followin# are a few notes that you should be aware of
when usin# these properties&
Re5uired property is set to True when a TTable is used and the database
schema specifies a field cannot contain a null. )t will be set to 0alse if a
T2uery is used as the dataset.
Re5uired and ReadOnly properties set on the application server are copied
to the client persistent T0ields on the initial T0ield creation in the client.
)f you chan#e the T0ield settin#s for these two fields on the server you
will need to either manually chan#e the values for the 'D* T0ields or
delete and re3add them to the 'D*.
When the Re5uired property is set to True you will #et one of two error
messa#es displayed when the record is saved and no value exists in the
field&
4. K0ield value re5uiredL
Re5uired property is set to True on the server
!o persistent T0ield for 'D* or field allows null values and
you manually set Re5uired to True
%. K0ield Hfld!ameF must have a valueL
0ield cannot contain null values as defined in table create
+nless the Re5uired property is explicitly set to 0alse in the
'D*
)f the Re5uired property is set to True on the 'D* but the table definition
allows nulls or the server T0ield has Re5uired set to 0alse
)f you want field3level validation you need to set the 'ustom'onstraint
property. The messa#e displayed will be the value of the
'onstraintError-essa#e property.
1dditional *Field properties used for constraints
The Options property on the TData*et/rovider supports the passin# of additional T0ield
properties. *ettin# po#ncField4rops to True adds to the data pac=et T0ield properties
specified in the Data*et property of the TData*et/rovider. The followin# are the T0ield
properties that can be used and are passed to the client based on the T0ield data type&
" Ali#nment
" 'urrency
" Display0ormat
" Display"abel
" DisplayWidth
" Edit0ormat
" Edit-as=
" -axCalue
" -inCalue
" Cisible
/sing *Data-et4rovider properties
)n addition to includin# T0ield properties the TData*et/rovider Options property also
includes the ability to set constraints for the entire dataset. Table 6 lists the options and a
brief description.
*able . " *Data-et4rovider Options property settings for dataset6level constraints
4roperty Description
poReadOnly Read3only result set
poDisable)nserts 'lient cannot insert records
poDisableEdits 'lient cannot edit records
poDisableDeletes 'lient cannot delete records
The poReadOnly settin# prevents any modifications to the data. !o visible indicator or
messa#e is #iven to the userD the data (ust cannot be modified.
When poDisable)nserts poDisableEdits or poDisableDeletes are set to True an
exception is raised when users attempt to insert modify or delete records. The exception
messa#e that is displayed contains the name of the 'D* and #nserts are not allowed for
)nserts Modifications are not allowed for edits and Deletes are not allowed for
deletes.
)f you do not want to have the exception dialo# displayed you need to trap for the
exception and chan#e how the error is displayed. 0or example the OnEditError or
OnDeleteError events can be used for edits and deletes. )n the client application there
are events for the OnEditError and OnDeleteError that exist in the code but the events
have not been assi#ned. 1ou can assi#n them yourself to see how you mi#ht trap for the
error and display a customiEed messa#e.
/sing *Data-et4rovider events
Three Data*et/rovider events can be used for constraint. These three are listed in Table
: with a brief description.
*able & " *Data-et4rovider events used to program constraints
vent Description
Before+pdateRecord Before each record is applied to the remote dataset
On+pdateData
Before the provider actually applies updates to the database of the entire
dataset received from the client
On+pdateError Errors on applyin# updates to database server
)n this section each of the three events are covered with some simple examples on how
they could be used.
/sing Before/pdateRecord
The Before+pdateRecord event is used when you want to validate data on an individual
record basis before chan#es are applied to the database. )t can also be used to modify the
client data before bein# saved. Below are the procedure parameters and a brief
description of what is passed to the event&
Before/pdateRecord?-ender: *Ob8ectA
-ourceD-: *Data-etA DeltaD-: *ClientData-etA
/pdateBind: */pdateBindA
var 1pplied: Boolean@A
*ender TData*et/rovider the tri##ered the event
*ourceD* the source data
DeltaD* data pac=et from client
+pdate7ind type of update& u=)nsert u=-odify u=Delete
Applied indicator that you set if you apply the update in the
Before+pdateRecord code yourself
*ome dos and don?ts for this event&
Don?t use Edit and /ost methods to modify and save data in the record
+se T0ield !ewCalue property to chan#e the value of a field
+se T0ield OldCalue to #et ori#inal value
+se Car)s!ull;0ield.!ewCalue< to determine if the field is !+""
+se Car)sEmpty;0ield.!ewCalue< to determine if the field has not chan#ed since
it was retrieved
)n the demo application the Before+pdateRecord is used to validate the order date field.
)f the order date is not empty its value is compared to the ship date to ensure the order
date is not after the ship date. To simplify the example only this condition is chec=ed
there is no chec= to see if the ship date is modified. The code below shows the procedure
used.
procedure T<!4!leC"nttraint.dpSale<ef"reUpdate4ec"rd(Sender:
TObject;
S"!rce)S: T)ataSet; )elta)S: TClient)ataSet; Update0ind: TUpdate0ind;
var #pplied: <""lean);
begin
{ NOTE: This example is only checking for changes to Order Date. }
if Update0ind =' !k)elete then
{ Check to see if Order Date is NU !sing "ar#sN!ll }
if ((>arI9!ll()elta)S.:ield<$9ame(*O4)E4?)#TE*).9e6>al!e) =
:ale) and
{ Check to see if Order Date has changed !sing "ar#sEmpty }
(>arIEmpt$()elta)S.:ield<$9ame(*O4)E4?)#TE*).9e6>al!e) =
:ale)) then
if )elta)S.:ield<$9ame(*O4)E4?)#TE*).9e6>al!e '
)elta)S.:ield<$9ame(*S;I+?)#TE*).Old>al!e then
raise E@cepti"n.Create(*Order )ate cann"t be after * -
*S3ip )ate. =<ef"reUpdate4ec"rd'*);
end;
The first comparison is to see if the action on the record was either an insert or update
there is no need to validate if the record is to be deleted. The +pdate7ind parameter is
used to see if the action to be ta=en is not u=Delete ;meanin# either an insert or update is
to be performed<. )f this is true the !ewCalue property of the ORDERJDATE T0ield is
used. !ewCalue is unassi#ned when no modification is made to the field. Car)s!ull and
Car)sEmpty are used to ensure the value is assi#ned and not null. )f this condition is true
the OldCalue property of the *I)/JDATE T0ield is used to compare to the updated
ORDERJDATE value. )f the order date is after the ship date an exception is raised.
)n this demo OldCalue is used because there is no chec=in# to see if the *I)/JDATE
field has also chan#ed. )n a complete validation you would need to chec= to see if both
fields chan#ed. OldCalue will always be the value that is currently in the client field.
1ou cannot use the Calue property. This property will be null when there is no chan#e to
the field therefore the need for the OldCalue property.
/sing On/pdateData event
The On+pdateData event occurs once in the event stream as updates start to be applied in
the application server. 1ou have access to all the records in the data pac=et that is
received from the client. "i=e the Before+pdateRecord you can validate data but you
have the entire dataset not (ust one record. Below are the procedure parameters and a
brief description of what is passed to the event&
OnUpdateData(Sender: TObject;
DataSet: TClientDataSet);
*ender TData*et/rovider the tri##ered the event
Data*et data pac=et from client
+nli=e the Before+pdateRecord event there is no parameter that indicates the type of
update. *ince you have the entire dataset you need to loo= at the status for each
individual record. To determine the status for the record +pdate*tatus a property of
T'lientData*et is used. Table A lists the possible values for +pdate*tatus with a brief
description.
*able 7 6 /pdate-tatus options and t%eir description
5alue Description
us+nmodified Record has not been modified.
us)nserted Record is an insert.
us-odified
Record has been modified. !ote& this record is the second of a matchin# pair. The
first will have a status of us+nmodified.
usDeleted Record to be deleted.
Before loo=in# at a code example of the On+pdateData event the contents of what is
passed should be covered.
A chan#e lo# is maintained by the 'D* for each insert update and delete. The 'D*
Delta property contains all records in the chan#e lo#. A separate record is added to the
lo# for each insert and delete. When an existin# record is modified two records are
entered in the lo#. The first record with a status of us/nmodified contains all field
values for the record before any modification was made. The second record with a status
of usModified contains only the field values that have chan#ed. All non3modified fields
are null in the second record. The 'D* Delta property is what the provider receives as
the Data*et property in the On+pdateData event.
)n the demo client pro(ect the second tab displays the contents of the chan#e lo#. 0i#ure
4B shows the lo# after performin# an edit insert delete and a second edit to a different
record. The +pdate*tatus field does not exist in the data it is a calculated field used to
display the status of each record.
Figure 1,: Client form displaying c%ange log
The first two records are a matchin# pair. The first record contains all the values of the
record before any chan#es were made. The second record with an +pdate*tatus of
-odified contains the values for every field in the record that chan#ed. )n this example
the *I)/JDATE field is chan#ed from $.6.4GG4 to $.:.4GG4.
The next two records are for an inserted and deleted record. On an insert any field where
data is entered is placed in the chan#e lo#. 0or deleted records the entire ori#inal field
values are placed in the lo#.
The last two records are a#ain a matchin# pair. Iere two fields are chan#ed the
*A"E*JRE/ and the *I)/JDATE.
Displayin# the chan#e lo# re5uires an extra 'D* in the application and a small amount of
code. The code that is used in the demo client is as follows&
procedure TfrmMain.+ageC"ntr"l&C3ange(Sender: TObject);
begin
if +ageC"ntr"l&.#cti5e+age = tb)elta then
/ith dmMain do
tr2
cdSale)elta.Cl"e;
cdSale)elta.)ata := cdSale.)elta;
cdSale)elta.Open;
e3cept
Meage)lg(*9" delta rec"rd e@it.*,mt.arning,/mbO01,();
end;
end;
The 'D* cds*ales contains the data from the provider. The 'D* for showin# the chan#e
lo# is named cds*alesDelta. When the second tab is selected the Delta property of
cds*ales is assi#ned to the Data property of cds*alesDelta. The try except bloc= is used
to display a simple messa#e when no modification has been made to the data.
To demonstrate the use of On+pdateData a similar validation used in the
Before+pdateRecord on the ORDERJDATE field is replicated. The code for the demo
On+pdateData event is shown below.
procedure T<!4!leC"nttraint.dpSaleUpdate)ata(Sender: TObject;
)ataSet: TClient)ataSet);
var
Old?S3ip)ate: T)ateTime;
begin
/ith )ataSet do
begin
:irt;
/hile not EO: do
begin
if UpdateStat! = !Unm"dified then
{ #n this demo$ only the modified records are %eing e&al!ated.
There 'ill %e t'o records in the data packet for each
modification.
The first record is the &al!es of all fields in the record
%efore
any changes are made. The second record of the pair contains
fields that are modified. (ny field that did not change is
n!ll. }
begin
{ )a&e the old )hip*Date field from the first record to
compare
to the modified Order*Date field. }
Old?S3ip)ate := )ataSet.:ield<$9ame(*S;I+?)#TE*).9e6>al!e;
9e@t;
{ Check to see if the modified record has Order Date
modified. #f so$ then &alidate the difference and
raise an exception if in&alid. }
if not >arIEmpt$()ataSet.:ield<$9ame(*O4)E4?)#TE*).9e6>al!e)
then
if :ield<$9ame(*O4)E4?)#TE*).9e6>al!e ' Old?S3ip)ate then
raise E@cepti"n.Create(*Order )ate cann"t be after S3ip
)ate. * -
*=OnUpdate)ata'*);
end;
9e@t; ++ ,o the next record in the delta packet
end;
end;
end;
A while loop is re5uired to process the entire dataset. 1ou start at the first record and
chec= its status. )f it is a non3modified record you =now that this record is the first of a
matchin# pair. The first record contains the ori#inal record valuesD therefore the
*I)/JDATE needs to be saved. To #et the updated field values !ext is called for the
dataset to move the modified record. )f the ORDERJDATE !ewCalue is not empty
then compare it to the saved *I)/JDATE value. An exception is raised if the order date
is after the ship date.
)n this example only modified records are chec=ed. )f a record is to be deleted or
inserted no validation is done. The while loop forces all records to be chec=ed until EO0
or the exception is raised.
/sing On/pdaterror event
The provider tri##ers the On+pdateError event if an error condition exists in the update
of a record. )n this event you can process the error attempt to fix it or chan#e the type of
messa#e that is displayed to the client. Below are the procedure parameters and a brief
description of what is passed to the event&
OnUpdateError(Sender: TObject;
DataSet: TClientDataSet;
E: EUpdateError;
UpdateKind: TUpdateKind;
var Response: TResolverResponse);
*ender TData*et/rovider that tri##ered the event
Data*et temporary dataset to access error record
E exception ob(ect
+pdate7ind type of update
Response what action to ta=e on the error when the event exits
"i=e the previous two events you use !ewCalue and OldCalue T0ield properties. 1ou
also use the 'urCalue property which indicates the current value in the database. This
allows you to see the currently stored value the ori#inal value the client received and the
updated value the client wants to apply.
The exception parameter has a property named Ori#inalException. )t allows you to #et
the ori#inal exception class. )f you are usin# the BDE and the ori#inal exception class is
EDBEn#ineError you can use Error'ode property to #et the error code value otherwise
you have to parse the messa#e.
)t is important to note that you should not chan#e the current record pointer in the
On+pdateError event.
The Response parameter has a different default value dependin# on the parameter
supplied in the Apply+pdates. )f the maximum number of errors allowed is Eero
Response defaults to rrAbort otherwise rr*=ip is assi#ned the default.
)n the demo server application On+pdateError is used to chan#e the error messa#es
returned from the database server. With )nterbase there is no specific error number to
evaluate for each individual error. 1ou need to loo= at the error text and determine from
it what error is raised. Two examples are used in the demo& )!TE@J69 is the error when
the ORDERJ*TAT+* field does not e5ual new open shipped or waitin# and
)!TE@J6: is the error when the order date is after the ship date.
The error messa#e
Ceneral -D3 error0
Operation violates C=CB constraint #+*C>.& on view or table -13-
is the default error messa#e for an order date that is after the ship date. -ost users would
not be able to identify the problem with the database error messa#e.
To improve the information displayed in the error messa#es the code below is used for
the On+pdateError event&
procedure T<!4!leC"nttraint.dpSaleUpdateErr"r(Sender: TObject;
)ataSet: TClient)ataSet; E: EUpdateErr"r; Update0ind: TUpdate0ind;
var 4ep"ne: T4e"l5er4ep"ne);
begin
if E.OriginalE@cepti"n is E)atabaeErr"r then
begin
if +"(*I9TEA?BC*,E.OriginalE@cepti"n.Meage) =' ( then
E.Message :=(*Order Stat! m!t be ne6, "pen, * -
*3ipped, "r 6aiting =OnUpdateErr"r'*)
else if +"(*I9TEA?BD*,E.OriginalE@cepti"n.Meage) =' ( then
E.Message := (*Order )ate cann"t be after S3ip )ate. * -
*=OnUpdateErr"r'*)
else
E.Message := (E.OriginalE@cepti"n.Meage - *=OnUpdateErr"r'*);
end;
end;
The Ori#inalExcpetion property of the E parameter is first chec=ed to determine if an
EDatabaseError occurred. )f this is true the -essa#e property is searched for a uni5ue
text strin# matchin# each of the database errors where a different error messa#e is to be
displayed. +sin# the )nterbase inte#rity identifiers ;)!TE@J69 and )!TE@J6:< a
distinction can be made. )f any other database error occurs that database error messa#e is
displayed. Replacin# the contents of the -essa#e property of the E+pdateError
parameter fully supports the reconcile error dialo# box from the Ob(ect Repository.
mpty field list for /4D1* statements
'ontents
*ome application re5uirements dictate the need to have fields available for input or
assi#nment on the client but the data in the fields do not update the database. These
fields can be created on the client side as internal calculated fields or they can be created
as calculated fields in the *2" statement retrievin# the data. )f these fields are the only
data that is modified in the 'D* executin# Apply+pdates results in a datapac=et sent to
the database but there are no fields to place in the +/DATE statement which results in
the followin# error reconcile dialo# messa#e usin# *2" *erver&
Incorrect syntax near 'se'
Runnin# the )nterBase D*/JDemo pro(ect #enerates the followin# error messa#e in a
reconcile error dialo#&
Token unknown - line 2, char -1
where
The real issue is the *2" that is #enerated by the D*/. The first part of the statement is
as follows&
update Session se
where
The Data*et/rovider attempts to create an +/DATE statement but the *2" statement
#enerated is incorrect and fails to execute. This Kempty fieldL list #enerates an exception
and causes the update to fail. Even if you exclude the fields usin# /rovider0la#s the
error still persists. The solution to this situation is to use the Before+pdateRecord event
and cycle throu#h all the fields in the datapac=et for each record and exclude the record
from #eneratin# an update when no chan#e is made to the actual data fields in the table.
Additional tables were added to the DBDE-O* database to be used for this and the next
example. These tables represent basic data for sessions at Bor'on. 0i#ure 49 shows the
tables and data types. Table 9 lists each table and how they are used.
Figure 1): Data tables used for final two topics
*able ) " *able name and description for e!ample
*able Description
Attendee Attendee names and id
*pea=er 'onference spea=er list
Attendee*ession *essions selected for an attendee
Trac=Type Trac=s offered ;e.#. Delphi NBuilder *tarTeam 'alilberR- etc<
*ession Available sessions title spea=er room time session type
*essionRoom Rooms used by each session
Room"ist "ist of available rooms at the 'onvention 'enter
!TE: The design of these tables was for demonstration purposes only and do not
represent the exact relationships required to fully support all possible session,
speaker and attendee relationships. For example, each session only supports a
single speaker and is assigned to a single track. nly a subset of the data from
the !onference was entered, "ust enough to use in demonstrating the technique
being demonstrated.
The code for this example is found on the Empty +pdate tab of the pro(ect shown in
0i#ure 46. The re5uirement here is to display all the sessions and which room or rooms
they are #oin# to use in the 'onvention 'enter. The Room"ist table has Room!o and
Room!ame fields. The actual names used are A4 B4 etc. 0or Bor'on this is a
strai#htforward namin# convention. )n some hotels.convention centers the names of the
rooms are names li=e Rainer Whidbey or other non3numerical names. The intent of the
example is to show how you would support the savin# of the Room!o in the data but
display the room name in the form.
Figure 1.: mpty /pdate tab
The *2" to retrieve the data from the *essions table and create the calculated field is as
follows&
-3C* -0;E C1-*?+/33 as 51RC=1R?1((@@ as Room+ames FROM -ession -
Once the data is retrieved the AfterOpen event on the 'lientData*et is used to translate
the room numbers into the full room description. The data is placed into the Room!ames
field.
-odifications to the rooms used by a session are done by clic=in# the ellipses button for
the row in the #rid. This displays a dialo# box where the rooms are selected and room
name list is placed into the Room!ames field and the room number list is assi#ned to the
Room)ds field.
'lic=in# the Open button on the Empty +pdate tab retrieves the data. )f you immediately
clic= the Apply+pdates the reconcile error dialo# is displayed with the error messa#e
3ine 1: #ncorrect synta! near FseF. This is caused by the chan#es created in the 'D*
AfterOpen. To prevent this error code needs to be added to the D*/
Before+pdateRecord. 'lic=in# the Enable B+R chec=box assi#ns the followin# code to
the D*/.
procedure TdmEmpt$Update.dpC!t"mer<ef"reUpdate4ec"rd(Sender: TObject;
S"!rce)S: T)ataSet; )elta)S: TC!t"mClient)ataSet;
Update0ind: TUpdate0ind; var #pplied: <""lean);
var
b#ll"6#ppl$: <""lean;
i: Integer;
begin
if Update0ind = !kM"dif$ then
begin
b#ll"6#ppl$ := :ale;
for i := ( to )elta)S.:ieldC"!nt % & do
b#ll"6#ppl$ := b#ll"6#ppl$ or
((not >arIClear()elta)S.:ield/i1.9e6>al!e)) and
(pfInUpdate in )elta)S.:ield/i1.+r"5ider:lag));
#pplied := not b#ll"6#ppl$;
end;
{NOTE: This is from an example %y -eff O&ercash on Team. }
end;
)t should be first noted that this techni5ue is based on an example from the Delphi forums
by Neff Overcash. Neff supplied this idea to a 5uestion raised on how to create a numeric
field that allows entry in the 'D*.
The basic techni5ue is to cycle throu#h all the fields in the datapac=et ;DeltaD*< and
determines if the field is to be updated ;pf)n+pdate in the /rovider0la#s property< and the
!ewCalue is assi#ned. )f all fields are chec=ed and there are no fields updated then
there is no need to create an update and the Applied parameter is set to True.
An alternative to this method is to turn lo##in# off before settin# the Room!ames values
modify the data then turn lo##in# bac= on. This is done by settin# the 'D* "o#'han#es
property to 0alse ma=e the chan#es then set "o#'han#es to True. This disables all
lo##in# while Room!ames is assi#ned.
This techni5ue can also be used when the *2" statement used to retrieve data in a one3to3
one relationship or a many3to3one relationship. )t is possible that fields from the KoneL
side of the relationship are updated thus cause the same condition as described above.
This same techni5ue can be used to determine the base table has no updates but the (oined
data has to be updated with an )!*ERT DE"ETE or +/DATE statement #enerated by
your code.
-ummary
'ontents
dbExpress and dbExpress.!ET allow both Win$% and .!ET Borland developers to easily
connect to many of the most popular databases used today. The power of both the
Data*et/rovider and 'lientData*et enable custom control over the data retrieval and data
modification to support the needs of today?s application re5uirement.

Вам также может понравиться