Skip to main content

Good practices when writing SQL in OneStream business rules

There are many cases where you will need to write queries to access data in your application database tables. We’ll provide both requirements that must be followed as well as recommendations for making your code and readable and maintainable as possible.

Check your queries and ensure that they are in a clearly readable format (string interpolation), use appropriate keyword casing, and include the WITH (NOLOCK) syntax, where applicable.

caution

TODO

(more content here)

Discuss the reasons for parameterizing SQL

Examples of parameterizing

why we should use dynamic SQL in business rules rather than embedded in the data adapter

discuss the option of using stored proceedures and why they can be beneficial

Explain the limitations of encrypting stored procedures to protect IP

The SQL_QueryBuilder class

If your solution consumes SQL data, whenever possible, try to include and use this provided helper class SQL_QueryBuilder. This may alleviate the heavy lifting associated with your application’s data access.

You can utilize the below helper class in your solutions.

SQL_QueryBuilder Code Examples

caution

TODO

C# CONVERSION STILL NEEDS TO BE REVIEWED

Public Class SQL_QueryBuilder

#Region "Properties"

Private Property SI As SessionInfo = Nothing
Private Property Query As New Text.StringBuilder
Public Property Params As New List(Of DbParamInfo)

#End Region

#Region "Constructors"

Public Sub New(si As SessionInfo)
Try
Me.SI = si

Catch ex As Exception
Throw ErrorHandler.LogWrite(Me.SI, New XFException(Me.SI, ex))
End Try
End Sub

#End Region

#Region "Conditions & Parameters"

Public Sub AddLine(ByVal newLine As String)
Try
Me.Query.AppendLine(newLine)

Catch ex As Exception
Throw ErrorHandler.LogWrite(Me.SI, New XFException(Me.SI, ex))
End Try
End Sub

Public Sub AddParameter(paramName As String, paramValue As Object)
Try
Me.Params.Add(New DbParamInfo(paramName, paramValue))

Catch ex As Exception
Throw ErrorHandler.LogWrite(Me.SI, New XFException(Me.SI, ex))
End Try
End Sub

Public Function CreateInClause(ByVal itemList As Object) As String
Try
Dim inClause As New Text.StringBuilder
Dim itemListType As Type = itemList.GetType()

'Validate The Object Is Of List Type
If itemListType.IsGenericType AndAlso itemListType.GetGenericTypeDefinition.IsAssignableFrom(GetType(List(Of))) Then

For Each i As Object In itemList

Dim paramCount As Integer = Me.Params.Count + 1

inClause.Append(If(inClause.Length = 0, $"@{paramCount}", $", @{paramCount}"))

Me.AddParameter(paramCount, i)

Next

Else

Dim msg As String = $"In Clause must be created from type List(Of T), not {itemListType}."

Throw New XFUserMsgException(SI, Nothing, Nothing, msg)

End If

Return inClause.ToString

Catch ex As Exception
Throw ErrorHandler.LogWrite(Me.SI, New XFException(Me.SI, ex))
End Try
End Function

Public Sub AddParamsToDbCommand(ByVal dbParamInfos As List(Of DbParamInfo), ByVal dbCommand As DbCommand)

If dbParamInfos IsNot Nothing Then

For Each dbParamInfo As DbParamInfo In dbParamInfos

Dim dbParameter As DbParameter = dbCommand.CreateParameter()

dbParameter.Direction = dbParamInfo.Direction
dbParameter.IsNullable = False
dbParameter.ParameterName = dbParamInfo.Name
dbParameter.Value = dbParamInfo.Value
dbCommand.Parameters.Add(dbParameter)

Next

End If

End Sub

#End Region

#Region "SQL Operations"

Public Function GetDataTable(Optional ByVal dbLocation As DbLocation = DbLocation.Application, Optional ByVal useCommandTimeoutLarge As Boolean = False, Optional ByVal includePrimaryKeyInfo As Boolean = False) As DataTable
Try
Dim dt As DataTable = Nothing

Select Case dbLocation

Case DbLocation.Application

Using dbConnApp As DbConnInfo = BRApi.Database.CreateApplicationDbConnInfo(Me.SI)

Using dbCommand As DbCommand = dbConnApp.CreateCommand(useCommandTimeoutLarge)

dbCommand.CommandText = Me.Query.ToString

Me.AddParamsToDbCommand(Me.Params, dbCommand)

dt = DbSql.GetDataTable(dbConnApp, dbCommand, includePrimaryKeyInfo, False)

End Using

End Using

Case DbLocation.Framework

Using dbConnFW As DbConnInfo = BRApi.Database.CreateFrameworkDbConnInfo(Me.SI)

Using dbCommand As DbCommand = dbConnFW.CreateCommand(useCommandTimeoutLarge)

dbCommand.CommandText = Me.Query.ToString
Me.AddParamsToDbCommand(Me.Params, dbCommand)
dt = DbSql.GetDataTable(dbConnFW, dbCommand, includePrimaryKeyInfo, False)

End Using

End Using

End Select

Return dt

Catch ex As Exception
Throw ErrorHandler.LogWrite(Me.SI, New XFException(Me.SI, ex))
End Try
End Function

Public Function ExecuteAndReturnDataTable(Optional ByVal tableName As String = Nothing, Optional ByVal dbLocation As DbLocation = DbLocation.Application, Optional ByVal useCommandTimeoutLarge As Boolean = False) As DataTable
Try
Dim dt As DataTable = Nothing

Select Case dbLocation

Case DbLocation.Application

Using dbConnApp As DbConnInfoApp = BRApi.Database.CreateApplicationDbConnInfo(Me.SI)

dt = BRApi.Database.ExecuteSql(dbConnApp, Me.Query.ToString, Me.Params, useCommandTimeoutLarge)

End Using

Case DbLocation.Framework

Using dbConnFW As DbConnInfoFW = BRApi.Database.CreateFrameworkDbConnInfo(Me.SI)

dt = BRApi.Database.ExecuteSql(dbConnFW, Me.Query.ToString, Me.Params, useCommandTimeoutLarge)

End Using

Case Else

Exit Select

End Select

If tableName IsNot Nothing Then dt.TableName = tableName

Return dt

Catch ex As Exception
Throw ErrorHandler.LogWrite(Me.SI, New XFException(Me.SI, ex))
End Try
End Function

Public Function ExecuteAndReturnDataReader(Optional ByVal tableName As String = Nothing, Optional ByVal dbLocation As DbLocation = DbLocation.Application, Optional ByVal useCommandTimeoutLarge As Boolean = False) As DataTable
Try
Dim dt As DataTable = Nothing

Select Case dbLocation

Case DbLocation.Application

Using dbConnApp As DbConnInfoApp = BRApi.Database.CreateApplicationDbConnInfo(Me.SI)

dt = BRApi.Database.ExecuteSqlUsingReader(dbConnApp, Me.Query.ToString, Me.Params, useCommandTimeoutLarge)

End Using

Case DbLocation.Framework

Using dbConnFW As DbConnInfoFW = BRApi.Database.CreateFrameworkDbConnInfo(Me.SI)

dt = BRApi.Database.ExecuteSqlUsingReader(dbConnFW, Me.Query.ToString, Me.Params, useCommandTimeoutLarge)

End Using

Case Else

Exit Select

End Select

If tableName IsNot Nothing Then dt.TableName = tableName

Return dt

Catch ex As Exception
Throw ErrorHandler.LogWrite(Me.SI, New XFException(Me.SI, ex))
End Try
End Function

Public Function ExecuteActionQuery(Optional ByVal dbLocation As DbLocation = DbLocation.Application, Optional ByVal useCommandTimeoutLarge As Boolean = False) As Long
Try
Select Case dbLocation

Case DbLocation.Application

Using dbConnApp As DbConnInfoApp = BRApi.Database.CreateApplicationDbConnInfo(Me.SI)

Return BRApi.Database.ExecuteActionQuery(dbConnApp, Me.Query.ToString, Me.Params, useCommandTimeoutLarge, True)

End Using

Case DbLocation.Framework

Using dbConnFW As DbConnInfoFW = BRApi.Database.CreateFrameworkDbConnInfo(Me.SI)

Return BRApi.Database.ExecuteActionQuery(dbConnFW, Me.Query.ToString, Me.Params, useCommandTimeoutLarge, True)

End Using

Case Else

Return -1

End Select

Catch ex As Exception
Throw ErrorHandler.LogWrite(Me.SI, New XFException(Me.SI, ex))
End Try
End Function

#End Region

#Region "Miscellaneous"

Public Function QueryString() As String

Return Query.ToString

End Function

Public Sub WriteQueryToLog()
Try
Dim msg As New Text.StringBuilder(Me.Query.ToString)

msg.AppendLine()
msg.AppendLine("Parameters:")

For Each param As DbParamInfo In Me.Params

msg = msg.Replace(String.Format("@{0}", param.Name), String.Format("'{0}'", param.Value.ToString))
msg.AppendLine(String.Format("{0} - {1}", param.Name, param.Value.ToString))

Next

BRApi.ErrorLog.LogMessage(Me.SI, msg.ToString)

Catch ex As Exception
Throw ErrorHandler.LogWrite(Me.SI, New XFException(Me.SI, ex))
End Try
End Sub

Public Sub Reset()

Params.Clear()
Query.Clear()

End Sub

#End Region

End Class

caution

TODO

Detail additional reasons why these helper classes can save time and increase productivity when writing SQL queries