3.8.4. Implementing the Customer Module
Modal forms are often used to add a new record or to edit an existing one. Once the modal form is closed by the mrOK
result, the changes are posted to the database. Database-aware visual components are usually used to create this kind of form. These components enable you to display the values of some fields from the current record and immediately accept the user’s changes in the corresponding fields if the dataset is in the Insert/Edit mode, i.e. before Post
.
The only way to switch the dataset to Insert/Edit mode is by starting a write transaction. So, if somebody opens a form for adding a new record and leaves for a lunch break, we will have an active transaction hanging until the user comes back from lunch and closes the form. This uncommitted edit can inhibit garbage collection, which will reduce performance. There are two ways to solve this problem:
Use the
CachedUpdates
mode, which enables the transaction to be active just for a very short period (to be exact, just for the time it takes for the changes to be applied to the database).Give up using visual components that are data-aware. This approach requires some additional effort from you to activate the data source and pass user input to it.
We will show how both methods are implemented. The first method is much more convenient to use. Let’s examine the code for editing a customer record:
procedure TCustomerForm.actEditRecordExecute(Sender: TObject);
var
xEditorForm: TEditCustomerForm;
begin
xEditorForm := TEditCustomerForm.Create(Self);
try
xEditorForm.OnClose := CustomerEditorClose;
xEditorForm.DataSource := Customers.DataSource;
xEditorForm.Caption := 'Edit customer';
Customers.Edit;
xEditorForm.ShowModal;
finally
xEditorForm.Free;
end;
end;
The Customers property is initiated in the OnCreate event:
procedure TCustomerForm.FormCreate(Sender: TObject);
begin
FCustomers := TDMCustomers.Create(Self);
DBGrid.DataSource := Customers.DataSource;
end;
We set the CachedUpdates
mode for the dataset in the Edit method of the dCustomers
module before switching it to the edit mode:
procedure TdmCustomers.Edit;
begin
qryCustomer.CachedUpdates := True;
qryCustomer.Edit;
end;
The logic of handling the process of editing and adding a record is implemented in the OnClose
event handler for the modal edit form:
procedure TCustomerForm.CustomerEditorClose(Sender: TObject;
var Action: TCloseAction);
begin
if TEditCustomerForm(Sender).ModalResult <> mrOK then
begin
Customers.Cancel;
Action := caFree;
Exit;
end;
try
Customers.Post;
Customers.Save;
Action := caFree;
except
on E: Exception do
begin
Application.ShowException(E);
// It does not close the window give the user correct the error
Action := caNone;
end;
end;
end;
To understand the internal processes, we can study the code for the Cancel
, Post
and Save
methods of the dCustomer
data module:
procedure TdmCustomers.Cancel;
begin
qryCustomer.Cancel;
qryCustomer.CancelUpdates;
qryCustomer.CachedUpdates := False;
end;
procedure TdmCustomers.Post;
begin
qryCustomer.Post;
end;
procedure TdmCustomers.Save;
begin
// We do everything in a short transaction
// In CachedUpdates mode an error does not interrupt the running code.
// The ApplyUpdates method returns the number of errors.
// The error can be obtained from the property RowError
try
trWrite.StartTransaction;
if (qryCustomer.ApplyUpdates = 0) then
begin
qryCustomer.CommitUpdates;
trWrite.Commit;
end
else
raise Exception.Create(qryCustomer.RowError.Message);
qryCustomer.CachedUpdates := False;
except
on E: Exception do
begin
if trWrite.Active then
trWrite.Rollback;
raise;
end;
end;
end;
Observe that the write transaction is not started at all until the OK button is clicked. Thus, the write transaction is active only while the data are being transferred from the dataset buffer to the database. Since we access not more than one record in the buffer, the transaction will be active for a very short time, which is exactly what we want.