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:

  1. 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).

  2. 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:

  1. procedure TCustomerForm.actEditRecordExecute(Sender: TObject);
  2. var
  3. xEditorForm: TEditCustomerForm;
  4. begin
  5. xEditorForm := TEditCustomerForm.Create(Self);
  6. try
  7. xEditorForm.OnClose := CustomerEditorClose;
  8. xEditorForm.DataSource := Customers.DataSource;
  9. xEditorForm.Caption := 'Edit customer';
  10. Customers.Edit;
  11. xEditorForm.ShowModal;
  12. finally
  13. xEditorForm.Free;
  14. end;
  15. end;
  16. The Customers property is initiated in the OnCreate event:
  17. procedure TCustomerForm.FormCreate(Sender: TObject);
  18. begin
  19. FCustomers := TDMCustomers.Create(Self);
  20. DBGrid.DataSource := Customers.DataSource;
  21. end;

We set the CachedUpdates mode for the dataset in the Edit method of the dCustomers module before switching it to the edit mode:

  1. procedure TdmCustomers.Edit;
  2. begin
  3. qryCustomer.CachedUpdates := True;
  4. qryCustomer.Edit;
  5. 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:

  1. procedure TCustomerForm.CustomerEditorClose(Sender: TObject;
  2. var Action: TCloseAction);
  3. begin
  4. if TEditCustomerForm(Sender).ModalResult <> mrOK then
  5. begin
  6. Customers.Cancel;
  7. Action := caFree;
  8. Exit;
  9. end;
  10. try
  11. Customers.Post;
  12. Customers.Save;
  13. Action := caFree;
  14. except
  15. on E: Exception do
  16. begin
  17. Application.ShowException(E);
  18. // It does not close the window give the user correct the error
  19. Action := caNone;
  20. end;
  21. end;
  22. end;

To understand the internal processes, we can study the code for the Cancel, Post and Save methods of the dCustomer data module:

  1. procedure TdmCustomers.Cancel;
  2. begin
  3. qryCustomer.Cancel;
  4. qryCustomer.CancelUpdates;
  5. qryCustomer.CachedUpdates := False;
  6. end;
  7. procedure TdmCustomers.Post;
  8. begin
  9. qryCustomer.Post;
  10. end;
  11. procedure TdmCustomers.Save;
  12. begin
  13. // We do everything in a short transaction
  14. // In CachedUpdates mode an error does not interrupt the running code.
  15. // The ApplyUpdates method returns the number of errors.
  16. // The error can be obtained from the property RowError
  17. try
  18. trWrite.StartTransaction;
  19. if (qryCustomer.ApplyUpdates = 0) then
  20. begin
  21. qryCustomer.CommitUpdates;
  22. trWrite.Commit;
  23. end
  24. else
  25. raise Exception.Create(qryCustomer.RowError.Message);
  26. qryCustomer.CachedUpdates := False;
  27. except
  28. on E: Exception do
  29. begin
  30. if trWrite.Active then
  31. trWrite.Rollback;
  32. raise;
  33. end;
  34. end;
  35. 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.