Instructions for the Implementation of Data Validation
DOCUMENTATION IS WORK IN PROGRESS
This wiki page is under construction. Information contained on it should not be relied on until this message is no longer present!!!
Overview
In OpenPetra, different aspects of Data Validation need to be implemented in several places to make the whole 'Data Validation Framework' work - on Client Side and Server Side.
The sections below detail what needs to be done and in which places to make Data Validation work. Following the way that is presented in those sections will help you in creating manual Data Validation and should help the whole development team to code Data Validation in a common and standardised way.
Automatic Data Validation is also in place in OpenPetra. Read this wiki page to learn about it.
Client Side
YAML File of a Form or UserControl
- For every Control to which manual Data Validation should be added, the Attribute Validation=true needs to be added.
- The 'Validation' Attribute is not available for Controls that group other controls, such as GroupBoxes, Panels, TabPages, UserControls, etc. - It is available only for individual Controls.
Examples
dtpDetailMailingDate: {Label=Mailing Date, Validation=True}
(found in \csharp\Petra\Client\MPartner\Gui\Setup\MailingSetup.yaml)
Once the YAML file gets (re-)generated with the WinForms Generator (using the nant generateWinforms
command), support for Data Validation will be added to the auto-generated C# code file of the Form/UserControl if the 'Validation' property has been set to 'true' for at least one Control. (If automatically generated Data Validation code for Controls is present in the auto-generated C# code file of the Form/UserControl, support for Data Validation will be added/will have been added to that file even if no manual Data Validation is specified.)
Multiple Controls involved in a single Data Validation
TODO
*.ManualCode.cs File
In order for the manual Data Validation to be doing something useful, a Method with one of the following Method names needs to be present in the *.ManualCode.cs file of the Form/UserControl:
private void ValidateDataManual(RowType ARow) private void ValidateDataDetailsManual(RowType ARow)
(where RowType is the Type of the Typed DataRow that is to be validated in this screen, e.g. 'PMailingRow' for a screen that edits 'p_mailing' DB Table records)
The name of the Method depends on whether the Data Validation of a Form/UserControl should validate data in a 'Details Section' (=a pnlDetails Panel).
- If it should not validate data in a 'Details Section', the name of the Method needs to be '
ValidateDataManual
' in order to validate the Form's/UserControl's data. - If it should validate data in a 'Details Section' then the name of the Method needs to be '
ValidateDataDetailsManual
' in order to validate the data in a 'Details Section'.- If data of the Form/UserContol outside of the 'Details Section' should be validated as well, then the Method named '
ValidateDataManual
' needs to be present in addition to the 'ValidateDataDetailsManual
' Method in order to validate the data outside of the 'Details Section'. An example of this can be found in\csharp\ICT\Petra\Client\MFinance\Gui\AP\APEditDocument.ManualCode.cs
- If data of the Form/UserContol outside of the 'Details Section' should be validated as well, then the Method named '
After adding this Method/these Methods, nant generateWinForms
needs to be run for the corresponding YAML file. This ensures that this Method/these Methods is/are called from the generated code of the Form/UserControl and that through that the manual Data Validation code will be run in all circumstances where it needs to be run.
The content of either of the Methods follows a standard pattern:
- The content of the first line is always
TVerificationResultCollection VerificationResultCollection = FPetraUtilsObject.VerificationResultCollection;
- A single empty line follows.
- A single call to a Method that is to be defined in a Shared DLL follows. That Method performs the actual Data Validation.
NOTE: Although the called Method could reside in a client-side DLL, doing so would make it impossible to perform the Data Validation from the server side! The same would be true if any other code would be added to the Method described in this section - it would be inaccessible from the server side. Since we want Data Validation to work both on client and on server side, the called Method (that contains the Data Validation code) needs to be placed in shared DLL's!
Sample code: (found in file \csharp\ICT\Petra\Client\MPartner\Gui\Setup\MailingSetup.ManualCode.cs)
private void ValidateDataDetailsManual(PMailingRow ARow) { TVerificationResultCollection VerificationResultCollection = FPetraUtilsObject.VerificationResultCollection; TSharedPartnerValidation_Partner.ValidateMailingSetup(this, ARow, ref VerificationResultCollection, FPetraUtilsObject.ValidationControlsDict); }
For information about the called Method that is to be defined in a Shared DLL and which performs the actual Data Validation please see the section Shared Libraries.
The Data Validation Method
The ValidateDataManual
/ ValidateDataDetailsManual
Method situated in the *.ManualCode.cs file of the Form/UserContro needs to call a Method in a shared DLL.
That called Method performs the actual Data Validation:
- If a Data Validation occurs, a TVerificationResult object is created
- the TVerificationResult object gets appended to a Collection that is held in the Form where the Data Validation is run.
- Once a Data Validation error got rectified by the user, the corresponding TVerificationResult object is removed from the Collection.
NOTE: Although this Method could reside in a client-side DLL, doing so will make it impossible to perform the Data Validation from the server side! Since we want Data Validation to work both on client and on server side, the Method that contains the Data Validation code needs to be placed in shared DLL's!
Typical Method signature:
public static void ValidateMailingSetup(object AContext, PMailingRow ARow, ref TVerificationResultCollection AVerificationResultCollection, TValidationControlsDict AValidationControlsDict)
The Method needs to have a ref VerificationResultCollection
Argument, among other Arguments. By modifying that Collection, this Method can add or remove specific TVerificationResult objects to/from the Form's Collection of TVerificationResult objects (which is always held in the FPetraUtilsObject.VerificationResultCollection object).
(The Method can optionally inspect that Collection for other TVerificationResult objects and evaluate those [that is needed only for complex data validation scenarios, though]. As the Collection will hold all TVerificationResult objects of the Form, TVerificationResult objects from other parts of the screen [namely those created by another UserControl, perhaps situated even on a different TabPage of the same Form] can be inspected!)
The content of the Method follows a standard pattern:
- There is a Variable declaration section. This is 'boiler plate' code.
- 1..n 'Data Validation Code Blocks' follow.
Each 'Data Validation Code Block' follows a standard pattern:
- The DataColumn that is to be validated is stored in the ValidationColumn Variable.
- A check is done whether that DataColumn should be validated at the current execution of the Method. This is done with a single line of 'boiler plate' code.
- If so,
- the actual data validation code is run,
- the addition/removal of the corresponding TVerificationResult is handled. Again, this is done with a single line of 'boiler plate' code.
The check whether the DataColumn should be validated seems strange, but that makes it possible to check for a different set of DataColumns, depending if the Method got called from the client-side or the server-side of OpenPetra.
Sample code: (found in \csharp\ICT\Petra\Shared\lib\MPartner\validation\Partner.Validation.cs, Method 'ValidateMailingSetup')
// 'MailingDate' must not be empty ValidationColumn = ARow.Table.Columns[PMailingTable.ColumnMailingDateId]; if (AValidationControlsDict.TryGetValue(ValidationColumn, out ValidationControlsData)) { // actual Data Validation code goes here // Handle addition to/removal from TVerificationResultCollection AVerificationResultCollection.Auto_Add_Or_AddOrRemove(AContext, VerificationResult, ValidationColumn); }
Data Validation Code for a single Data Validation Code Block
The data validation code uses a pattern that is presented here. It is easy enough and can be followed in most data validation scenarios. For scenarios that require a different methodology, code that has the same net effect needs to be written.
Net effect of the data validation code:
- If the data validation fails, a
TVerificationResult
instance is to be created and it is to be assigned to the localVerificationResult
Variable; - If the data validation succeeds, the local
VerificationResult
Variable must not be assigned any value. Alternatively,null
can be assigned to the localVerificationResult
Variable (it isnull
by default).
Sample code: (found in file \csharp\ICT\Petra\Shared\lib\MPartner\validation\Partner.Validation.cs, Method 'ValidateMailingSetup')
VerificationResult = TDateChecks.IsNotUndefinedDateTime(ARow.MailingDate, ValidationControlsData.ValidationControlLabel, true, AContext, ValidationColumn, ValidationControlsData.ValidationControl);
This sample code uses one of the many predefined Data Validation Routines (TDateChecks.IsNotUndefinedDateTime
). Most of these routines are found in the Ict.Common.Verification
Namespace. Available Classes in this Namespace: TDateChecks
, TNumericalChecks
, TStringChecks
. The Methods in these Classes are all static and all behave the same, therefore they are easy to use once one has used them a bit. Another Class is available as well, TGuiChecks
, which also contains static Methods, but they work differently and have different purposes. Simply have a look around these Classes to familiarise yourself with what they provide.
Sample code where no predefined Data Validation Routine is used: (found in \csharp\ICT\Petra\Shared\lib\MPartner\validation\Partner.Validation.cs, Method 'ValidateSubscriptionManual')
if (((!ARow.IsSubscriptionStatusNull()) && (ARow.SubscriptionStatus == String.Empty)) || (ARow.IsSubscriptionStatusNull())) { VerificationResult = new TScreenVerificationResult(new TVerificationResult(AContext, ErrorCodes.GetErrorInfo(PetraErrorCodes.ERR_SUBSCRIPTION_STATUSMANDATORY)), ValidationColumn, ValidationControlsData.ValidationControl); } else { VerificationResult = null; }
Utilising Special Functionality
Accessing a Cacheable DataTable
By using static Methods of the Ict.Petra.Shared.TSharedDataCache
Classes' Subclasses (MPartner
, MFinance
, etc.) one can retrieve the content of a Cacheable DataTable and perform Data Validation checks based on that. The calls to these static Methods will succeed no matter if the Data Validation code is run client-side or server-side - the respective Cache Manager (client-side or server-side) is used automatically for the retrieval of the Cacheable DataTable!
Sample code: (found in file '\csharp\ICT\Petra\Shared\lib\MPersonnel\validation\Personnel.Validation.cs', Method 'ValidateJobAssignmentManual')
TypeTable = (PtAssignmentTypeTable)TSharedDataCache.TMPersonnel.GetCacheableUnitsTable (TCacheableUnitTablesEnum.JobAssignmentTypeList); TypeRow = (PtAssignmentTypeRow)TypeTable.Rows.Find(ARow.AssignmentTypeCode); // 'Assignment Type' must not be unassignable if ((TypeRow != null) && TypeRow.UnassignableFlag && (TypeRow.IsUnassignableDateNull() || (TypeRow.UnassignableDate <= DateTime.Today))) { VerificationResult = new TScreenVerificationResult(new TVerificationResult(AContext, ErrorCodes.GetErrorInfo(PetraErrorCodes.ERR_VALUEUNASSIGNABLE_WARNING, new string[] { ARow.AssignmentTypeCode })), ValidationColumn, ValidationControlsData.ValidationControl); }
Accessing Server-side Functionality that is exposed to the Client
Some Data Validations need to access server-side functionality (e.g. to load data from the database that is not contained in a Cacheable DataTable).
Since all Data Validations need to be working in both the client- and server side Data Validation scenarios, a 'Helper' Method needs to be created that is called by the Data Validation code, and that Helper Method in turn needs to utilise a Delegate to perform the actual Method call:
- If the Data Validation code is run in the server-side context, the Delegate will need to be set up to simply call the server-side Method.
- If the Data Validation code is run in the client-side context, the Delegate will need to be set up to call a client-side Method that in turn will need to call the server-side Method.
Example: Calling server-side Method 'TServerLookup.TMPartner.VerifyPartner'
- Helper Class:
\csharp\ICT\Petra\Shared\lib\MPartner\validation\Helper.cs
, Class 'TSharedPartnerValidationHelper
'public static TVerifyPartner VerifyPartnerDelegate
: The Delegate that needs to be set up at Client startup time (client side)/at Client AppDomain creation time (server side)\csharp\ICT\Petra\Client\app\MainWindow\PetraClientMain.cs, InitialiseClasses
Method:TSharedPartnerValidationHelper.VerifyPartnerDelegate = @TServerLookup.TMPartner.VerifyPartner;
\csharp\ICT\Petra\Server\lib\CallForwarding\CallForwarding.cs, TCallForwarding Class
, static Constructor:TSharedPartnerValidationHelper.VerifyPartnerDelegate = @TPartnerServerLookups.VerifyPartner;
public static bool VerifyPartner
: Method that can be called by Data Validation Methods. It transparently executes the Delegate on behalf of the calling Method.
- Data Validation:
\csharp\ICT\Petra\Shared\lib\MPartner\validation\Partner.Validation.cs, Method 'IsValidPartner'
- Calls Method '
VerifyPartner
'. This works no matter if the Data Validation is run in the client or server context!
- Calls Method '
Note: Although setting up a Helper Class and initialising the Delegate means some work, we foresee that many Helper Classes should be able to be re-used. Therefore we should aim to make the Helper classes as universal as possible.
Data Validation Methods need to be placed in shared DLL's according to the following rules:
- All Data Validation Methods need to be placed in OpenPetra-Module-specific DLL's:
- Ict.Petra.Shared.lib.MCommon.validation.DLL, Ict.Petra.Shared.lib.MFinance.validation.DLL, Ict.Petra.Shared.lib.MPartner.validation.DLL, Ict.Petra.Shared.lib.MPersonnel.validation.DLL, Ict.Petra.Shared.lib.MSysMan.validation.DLL
- The filename and the Namespace of the Class that contains the Data Validation Methods needs to correspond to the Namespace that the Data Validation pertains to.
- The Classes need to be Partial Classes. This is because automatically generated code augments the manually written code in the same Class.
- Filenames: There is a differentiation between the files that are manually created and the files that are automatically generated.
- Examples:
- Validation of MCommon DataTables takes place in the (partial) Class '
TSharedCommonValidation
' in file\csharp\ICT\Petra\Shared\lib\MCommon\validation\Common.Validation.cs
(manually created file); 'Common.Validation-generated.cs' (automatically generated file). - Validation of MFinance Gift DataTables takes place in the (partial) Class '
TSharedFinanceValidation_Gift
' in file\csharp\ICT\Petra\Shared\lib\MFinance\validation\Gift.Validation.cs
(manually created file); 'Gift.Validation-generated.cs' (automatically generated file). - Validation of MPartner Partner DataTables takes place in the (partial) Class '
TSharedPartnerValidation_Partner
' in file\csharp\ICT\Petra\Shared\lib\MPartner\validation\Partner.Validation.cs
(manually created file); 'Partner.Validation-generated.cs' (automatically generated file).
- Validation of MCommon DataTables takes place in the (partial) Class '
- Examples:
- Special case: Data Validation Methods that validate data that is contained in Cacheable DataTables are all to be placed in Ict.Petra.Shared.lib.MCommon.validation.DLL, (partial) Class '
TSharedValidation_CacheableDataTables
' in File '\csharp\ICT\Petra\Shared\lib\MCommon\validation\Cacheable.Validation.cs
' - regardless of the OpenPetra-Module they pertain to. Automatically generated code is all placed in the file 'Cacheable.Validation-generated.cs' in the same Namespace and DLL.
- The filename and the Namespace of the Class that contains the Data Validation Methods needs to correspond to the Namespace that the Data Validation pertains to.
- Ict.Petra.Shared.lib.MCommon.validation.DLL, Ict.Petra.Shared.lib.MFinance.validation.DLL, Ict.Petra.Shared.lib.MPartner.validation.DLL, Ict.Petra.Shared.lib.MPersonnel.validation.DLL, Ict.Petra.Shared.lib.MSysMan.validation.DLL
Server Side
TODO