Coding Best Practices Part 1
This blog outlines coding practices that any professional developer using the finPOWER Connect API should adhere to.
finPOWER Connect is written entirely in VB.NET. All sample scripts are also written in VB.NET. Although C# is an option, Intersoft Systems does not provide support for this.
In this first blog we will look at coding practices.
Related Blogs
More information is available in the finPOWER Connect Programming Guide.
Top Best Practices
- Always check return values
- Never perform long running code or call external services within a Database Transaction
- Never use message boxes or any other User Interface components in Scripts that do not expose the User Interface layer, and never within a Database Transaction
- Always (where possible) use Global Collections rather than loading an object
- Option Strict should be set on
- Option Explicit should be set on
Always check return values
Many methods return either TRUE or FALSE depending on whether they have succeeded. This value should always be checked.
The more complicated and nested the code is, the harder it will be to track errors if you don't check return values.
Private Function Account_AddPaymentArrangement(accountPk As Integer,arrangementDate As Date,arrangementByWhom As String,arrangementType As String,arrangementReason As String,paymentCycle As String,Optional paymentNextDate As Date = Nothing,Optional paymentOverride As Decimal = 0) As BooleanDim AccountPayArrangementAdd As finAccountPayArrangementAddDim Success As Boolean' Assume successSuccess = True' InitialiseAccountPayArrangementAdd = finBL.CreateAccountPayArrangementAdd()' Create Payment ArrangementWith AccountPayArrangementAdd' Load AccountSuccess = .AccountLoadPk(accountPk)' Clear existing PromisesIf Success ThenSuccess = .PromisesClear()End IfIf Success Then' Update Properties.ArrangementByWhom = arrangementByWhom.ArrangementDate = arrangementDate.ArrangementReason = arrangementReason.ArrangementType = arrangementType.OverdueHold = True.PrintAdvice = False' Update CalculationWith .CalculationIf Len(paymentCycle) <> 0 Then .PaymentCycle = paymentCycleIf paymentNextDate <> Nothing Then .PaymentNextDate = paymentNextDateIf paymentOverride <> 0 Then .PaymentRegularOverride = paymentOverrideEnd With' CalculateSuccess = .Calculate()End If' Commit Payment ArrangementIf Success ThenSuccess = .ExecuteCommit()End IfEnd With' Return SuccessReturn SuccessEnd Function
Generally, if a method returns FALSE, an error message will have been set. Exceptions to this rule include methods used to check for the existence of values, for example:
ExistsExistsPkHasValues
Certain properties, usually collections, are loaded on demand, finAccount.Transactions for example.
Accessing these collections directly is fine in most situations, for example to display a list of transactions. However, for situations that rely on the collection to have been loaded correctly, the property's corresponding 'Load' method should first be called and the return value checked.
Dim Account As finAccountDim Success As Boolean' Assume SuccessSuccess = True' Check the Transactions LoadIf Success ThenSuccess = Account.TransactionsLoad()End IfIf Success Then' Do SomethingEnd If' Return SuccessReturn Success
Database Transactions
Database Transactions can have an affect on performance and data integrity. Database Transactions are used to ensure all related operations are completed or undone as a whole.
- Long running tasks within a Database Transaction will affect the performance of the system as it will lock database resources for the length of the task. This stops other users from accessing these resources.
- Calls to external services within a Database Transaction can result in data integrity issues if the Database Transaction is rolled back. This is because the external service may have been updated but the finPOWER Connect database doesn't reflect this.
- Calls to User Interface components, such as a Message Box, within a Database Transaction may cause the Script to stop and wait for user interaction which in turn will affect performance as the database resources will be locked until the user interaction has been completed.
For example, never post payments to an external banking API and update data in finPOWER Connect. If the script fails and rolls back the Database Transaction the external banking API may have already transferred money but finPOWER Connect hasn't (as it was undone) - and finPOWER Connect will send the duplicate payment again.
Dim Success As Boolean' Assume SuccessSuccess = True' Begin TransactionIf finBL.Database.TransactionBegin() Then' Call External Service that updates stateSuccess = CallExternalService()' Update flag in finPOWER Connect to day it is done, but it failsIf Success ThenSuccess = CallMethodThatFails()End If' Commit or Rollback TransactionIf Success ThenfinBL.Database.TransactionCommit()Else' Transaction rollback leaves the external service with data in a different statefinBL.Database.TransactionRollback()End IfElseSuccess = FalseEnd If' Return SuccessReturn Success
User Interface Components
Calls to User Interface objects, such as a Message Box, must never be used in Database Transactions or unattended Scripts as the Script will not be able to complete until the user interaction has been completed.
An example of a call to a User Interface object, in this case a Message Box, within a Database Transaction. This will cause performance issues as the Database Transaction will lock tables until the user interaction is completed:
Dim Success As Boolean' Assume SuccessSuccess = True' Begin TransactionIf finBL.Database.TransactionBegin() Then' Do something' Display Message Box which will hold the transaction lockMsgBox("Hello World")' Do something' Commit or Rollback TransactionIf Success ThenfinBL.Database.TransactionCommit()ElsefinBL.Database.TransactionRollback()End IfElseSuccess = FalseEnd If' Return SuccessReturn Success
Global Collections
finPOWER Connect preloads most Admin files into "Global Collections" which are held in memory. Wherever possible use a Global Collection rather than loading information a second time from the database. As well as improving performance, using Global Collections makes coding simpler and more compact.
The following code illustrates loading an object:
Dim Element As finElementDim Success As Boolean' Assume SuccessSuccess = True' Load ElementIf Success Then' Create ElementElement = finBL.CreateElement()Success = Element.Load("FEE")End If' ProcessIf Success ThenIf Element.Active Then' Do SomethingEnd IfEnd If' Return SuccessReturn Success
The following code illustrates using the Global Collection:
Dim Success As Boolean' Assume SuccessSuccess = True' Check that the Element exists and it is activeIf finBL.Elements.Exists("FEE") AndAlso finBL.Elements("FEE").Active Then' Do SomethingEnd If' Return SuccessReturn Success
Option Strict
Option Strict requires that all declared variables have a data type specified and will restrict implicit data type conversions to only widening conversions and also disallow late binding.
Normally, setting one variable to another variable of a different type indicates a programming error. However, Visual Basic allows conversions of many data types to other data types. This can result in:
- Data loss - this is when the value is converted to a data type with less precision or a smaller capacity;
- Run-time error - this occurs when a narrowing conversion fails.
Option Strict ensures a compile-time notification of these narrowing conversions so that they can be avoided.
The default is Off therefore you must turn it on to use it, this can be achieved by adding the following code to the top of your Script code:
Option Strict On
Narrowing data type conversions can cause a compile-time error:
Dim IntegerVariable As IntegerDim LongVariable As Long' Set a Long variableLongVariable = Long.MaxValue' Set Integer variable to value of Long variableIntegerVariable = LongVariable' This results in a narrowing conversion' With Option Strict on a compile error is encountered:' Option Strict On disallows implicit conversions from 'Long' to 'Integer'
Option Explicit
Option Explicit requires that all variables are declared. If you attempt to use an undeclared variable name an error will occur at compile time.
To turn it on add the following code to the top of your Script code:
Option Explicit On
Variables must have a data type specified:
' Use a variable that is not declaredLongVariable = Long.MaxValue' With Option Explicit on a compile error is encountered:' 'LongVariable' is not decalred. It may be inaccessable due to its protection level.
versus
Dim LongVariable As LongLongVariable = Long.MaxValue