What is proper way to handle concurrent updates with lmOptimistic?

Discussion of open issues, suggestions and bugs regarding UniDAC (Universal Data Access Components) for Delphi, C++Builder, Lazarus (and FPC)
Post Reply
PaulAtMicrolynx
Posts: 2
Joined: Mon 28 Oct 2019 17:26

What is proper way to handle concurrent updates with lmOptimistic?

Post by PaulAtMicrolynx » Wed 08 Jul 2020 14:28

We are using UniDAC with Embarcadero C++Builder 10.3.3. Our code works but it depends on detecting literal exception message text "Record was changed by another user". If this text changes in a future release then our application will fail and we will not know unless we test for it or our client reports an issue. What is the proper way to handle this situation?

There can be multiple instances of our application which can concurrently updated the same record. When this happens one instance succeeds and the others fail by throwing an exception. After some experimenting with the available handlers and exceptions we determined that detecting the message string in the catch( EDatabaseError& e ) worked. What is the proper way to do this that will work even if the exception message text changes?

Here is the VCL .dfm file text for one of the tables and its data source:

Code: Select all

  object UniTblManufacturer: TUniTable
    TableName = 'Manufacturer'
    Connection = UniConICS2
    Left = 35
    Top = 71
    object UniTblManufacturerManufacturerID: TIntegerField
      AutoGenerateValue = arAutoInc
      FieldName = 'ManufacturerID'
      ReadOnly = True
      Required = True
      Visible = False
    end
    object UniTblManufacturerManufacturerName: TStringField
      DisplayLabel = 'Manufacturer Name'
      FieldName = 'ManufacturerName'
      Required = True
      Size = 30
    end
  end
  object UniManufacturersSource: TUniDataSource
    DataSet = UniTblManufacturer
    Left = 147
    Top = 71
  end
Note the default settings include:
LockMode = lmOptimistic
RefreshOptions empty (i.e. does not include roAfterUpdate)

Here is the relevant code which is meeting client requirements:

Code: Select all

    bool bUpdateConflict = false;

    try
    {
        UniTblManufacturer->Post();
    }
    catch( EUniError& e )
    {
        sErrMsg = e.Message;
    }
    catch( EDatabaseError& e )
    {
        if( e.Message.Compare( "Record was changed by another user" ) == 0 )
            bUpdateConflict = true;

        sErrMsg = e.Message;
    }
    catch(...)
    {
        sErrMsg = String( "Unspecified exception." );
    }

    if( bUpdateConflict )
        // Handle the update conflict:
        //     Warn user and display the current field values.
        //     Ask user to review field values, reenter desired changes, and try update again.

oleg0k
Devart Team
Posts: 190
Joined: Wed 11 Mar 2020 08:28

Re: What is proper way to handle concurrent updates with lmOptimistic?

Post by oleg0k » Fri 10 Jul 2020 11:16

Hello,

Your code is correct. The EDatabaseError exception saying that "Record was changed by another user" is raised by our components, therefore it doesn't contain any error codes. We have no plans to change the message.

wbr, Oleg
Devart Team

PaulAtMicrolynx
Posts: 2
Joined: Mon 28 Oct 2019 17:26

Re: What is proper way to handle concurrent updates with lmOptimistic?

Post by PaulAtMicrolynx » Fri 10 Jul 2020 20:47

Thank you oleg0k.

In that case, can my code detect when that string changed or might have changed? If the component doesn't give me direct access to a variable that defines what that string is then can my code at least determine which version of UniDAC is being used so that it can warn the programmer that this needs to be retested?

Thanks

oleg0k
Devart Team
Posts: 190
Joined: Wed 11 Mar 2020 08:28

Re: What is proper way to handle concurrent updates with lmOptimistic?

Post by oleg0k » Thu 16 Jul 2020 13:47

Hello,
To retrieve the version of the used UniDAC, you can use the UniDACVersion constant declared in the UniDacVer.inc file.
To use the UniDACVersion constant in a UniDAC edition without source code, it is enough to add the Uni unit to the USES clause of your unit:

Code: Select all

ShowMessage(Uni.UniDACVersion)
We have no plans to change the message.

wbr, Oleg
Devart Team

Post Reply