Business Entity Validation made easy
Validation of data entered by a user or received from a foreign system is a key function of every business application. The purpose of validation is to ensure data integrity and to inform the user with a meaningful message if for instance a mandatory field is not filled or a value violates the business rules. Validation can be executed on the client or on the server. Client side validation should give the user a more promptly reaction avoiding additional AppServer calls. The validation of mandatory fields could be executed on the client side for instance in a viewer control (VisualValidate method) and does not require submitting the data to the server. However this is not the most suited location for any kind of validation. In order to have the same validation executed in different contexts like OpenEdge GUI for .NET, OpenEdge Mobile or batch data import you’d have to reimplement the same validation in all those contexts. On the server side (ValidateData method in the BusinessEntity class or the validation hooks in the Data Access object) those validations can be implemented in a central location and ensure execution of this criticial pieces of business logic in any context the BusinessEntity may be used in. Furthermore the validation on the server has the ability to verify data integrity in relation to existing data in the database (i.e. referential integrity).
The Consultingwerk.OERA.Validate class has been developed to simplify standard validation scenarios in the BusinessEntity’s ValidateData method. This class provides static methods that can be used in an Assertion style to implement validation logic.
As an example for using the Validate class methods you will see following an example ValidateData method verifying changes in one or more Customer records. You will see validations of the Customer Name and the assigned SalesRep key (referential integrity).
We start by adding the following USING to the Business Entity. Due to this, you will see in the sample code that the Consultingwerk.OERA.Validate class is only referenced by the class name itself, thus allowing the developer to focus more on the actual business rules.
Note: This USING statement is contained in the standard templates of the Business Entity Designer so that most Business Entities should contain this statement already.
USING Consultingwerk.OERA.* FROM PROPATH . /*------------------------------------------------------------------------------ Purpose: Provides a hook for high level data validation before Update operations Notes: Invoked during SaveChanges (). When the ERROR flag of the ProDataset is set, the Update operation will be cancelled before writing back the data to the database using the DataAccess object ------------------------------------------------------------------------------*/ METHOD OVERRIDE PUBLIC VOID ValidateData (): FOR EACH eCustomer ON ERROR UNDO, THROW: Validate:IsNotNullOrEmpty (BUFFER eCustomer:HANDLE, "Name":U, MessageFormatter:GetMessage ("VALMSG":U, 1, "Customer Name")). IF Validate:IsNotNullOrEmpty (BUFFER eCustomer:HANDLE, "SalesRep":U, MessageFormatter:GetMessage ("VALMSG":U, 1, "Sales Rep")) THEN Validate:CanFind (BUFFER eCustomer:HANDLE, "SalesRep":U, "Consultingwerk.SmartComponentsDemo.OERA.Sports2000.SalesRepBusinessEntity":U, "eSalesRep":U, SUBSTITUTE ("FOR EACH eSalesRep WHERE eSalesRep.SalesRep = &1":U, QUOTER (eCustomer.SalesRep)), MessageFormatter:GetMessage ("VALMSG":U, 2, "Sales Rep", eCustomer.Salesrep)). END. END METHOD.
As you can see, all records of the eCustomer TEMP-TABLE are processed. This logic is used to process a dataset with changes received from a consumer, so that all changed or created records will be validated. The different validation methods return TRUE or FALSE depending on if the validation was successful or not. When validating a record, the return value (LOGICAL) of one of the prior validations can be used to see if further validation on this record is useful and can be processed.
As an example, when the Salesrep field of the Customer table is mandatory but not populated, there is no need in searching for the related customer record.
You can see this when validating if the SalesRep field IsNotNullOrEmpty.
Validate:IsNotNullOrEmpty (BUFFER eCustomer:HANDLE, "SalesRep":U, MessageFormatter:GetMessage ("VALMSG":U, 1, "Sales Rep")) .
As you can see in the sample above (full ValidateData method): if the field is filled with a value the next validation will take place. The second validation is to prove that the SalesRep entered exist in the DB.
Validate:CanFind (BUFFER eCustomer:HANDLE, "SalesRep":U, "Consultingwerk.SmartComponentsDemo.OERA.Sports2000.SalesRepBusinessEntity":U, "eSalesRep":U, SUBSTITUTE ("FOR EACH eSalesRep WHERE eSalesRep.SalesRep = &1":U, QUOTER (eCustomer.SalesRep)), MessageFormatter:GetMessage ("VALMSG":U, 2, "Sales Rep", eCustomer.Salesrep)) .
The CanFind validation method queries a Business Entity and returns true if a record with the requested value is available.
This functionality can also be used in the DataAccess classes when validating values while populating the dataset or before saving changes.
Currently there are three different types of methods for validating values.
The first type of validation methods is used to compare the value of a field from a dataset with a value provided in the call to the method. The following methods are available for that GE, GT, LE, LT. The parameter lists of the various method implementations are identical except that the data type of the value to compare the field value with.
- phDatasetBuffer The handle to the ProDataset Buffer
- pcFieldName The field name to validate
- pxValue The value to compare (data type needs to be compatible with the buffer field that was compared)
- pcErrorMessage The error message that should be returned to the user, optionally using &1 as a place holder for the field value and &2 for the compared value
The second type of validation methods does not need values to compare with, but has predefined functionality to validate the values. There are the methods IsEmpty, IsNotNullOrEmpty, IsNotUnknownOrZero, IsUnknown, IsUnknownOrZero, IsWeekday, IsZero, MaxLength and MinLength implemented in the Validate class.
- phDatasetBuffer The handle to the ProDataset Buffer
- pcFieldName The field name to validate
- pcErrorMessage The error message, optionally using &1 as a place holder for the field value
The third type of validation methods is CanFind. In this method the ServiceInterface (Server side) is used to execute a FetchData request to a Business Entity. If a record is returned by the FetchData request, the CanFind method returns true. Otherwise it returns false.
- phDatasetBuffer The handle to the ProDataset Buffer
- pcFieldName The field name to validate (the field's value may be used for &1 in the pcErrorMessage)
- pcBusinessEntityName The name of the Business Entity
- pcTempTableName The name of the temp-table
- pcQueryString The query string, e. g. "FOR EACH eSalesRep WHERE eSalesrep.SalesRep = 'BBB'"
- pcErrorMessage The error message, optionally using &1 as a place holder for the field value
For a detailed documentation of the Validate class see http://help.consultingwerkcloud.com/smartcomponent_library/release/.
What all methods have in common is, that when the validation fails, the ProDataset’s ERROR flag is set to true, the Buffer’s ERROR flag is set to true and the error message is added to the Buffers ERROR-STRING attribute using the Consultingwerk.Util.DatasetHelper AddErrorString method.
When additional validations are required that are not implemented in the Validate class, you can just perform your validation based on the ProDataset temp-table record and add error messages as required to the ERROR-STRING of the Buffer. It’s recommended to use the AddErrorString method of the DatasetHelper class for this purpose.