Imports DigitalData.Modules.Database
Imports DigitalData.Modules.EDMI.API
Imports DigitalData.Modules.EDMI.API.Constants
Imports DigitalData.Modules.EDMI.API.EDMIServiceReference
Imports DigitalData.Modules.Logging
Imports DigitalData.Modules.Language.Utils
Public Class DatabaseWithFallback
Private ReadOnly _Logger As Logger
Private ReadOnly _Client As Client
Private ReadOnly _ClientConfig As GlobalStateClientConfiguration
Private ReadOnly _DatabaseECM As MSSQLServer
Private _DatabaseIDB As MSSQLServer
'''
''' Options for GetDatatable
'''
Public Class GetDatatableOptions
Public ReadOnly FallbackSQL
Public ReadOnly FallbackType
'''
''' Filter expression for the cached Datatable
'''
Public Property FilterExpression As String = ""
'''
''' Columns to sort the cached Datatable by
'''
Public Property SortByColumn As String = ""
'''
''' Force the fallback, skipping the service completely
'''
Public Property ForceFallback As Boolean = False
'''
''' Connection Id to use, references TBDD_CONNECTION
'''
Public Property ConnectionId As Integer = 0
'''
''' Creates a new options object for GetDatatable options
'''
''' SQL Command to execute as fallback
''' Named Database to use for the fallback SQL Command
Public Sub New(pFallbackSQL As String, pFallbackType As Constants.DatabaseType)
FallbackSQL = pFallbackSQL
FallbackType = pFallbackType
End Sub
End Class
Public Sub New(LogConfig As LogConfig, Client As Client, DatabaseECM As MSSQLServer, DatabaseIDB As MSSQLServer)
_Logger = LogConfig.GetLogger()
_Client = Client
_DatabaseECM = DatabaseECM
_DatabaseIDB = DatabaseIDB
_Logger.Debug("Client exists: [{0}]", Not IsNothing(Client))
_Logger.Debug("DatabaseECM exists: [{0}]", Not IsNothing(DatabaseECM))
_Logger.Debug("DatabaseIDB exists: [{0}]", Not IsNothing(DatabaseIDB))
' Load client config, will throw if client is not yet connected to service
_ClientConfig = Client?.ClientConfig
End Sub
'''
''' Set the IDB Database class after initializing the class.
''' It is now your responsibility to make sure to not use any IDB calls before calling this method.
'''
Public Sub InitializeIDB(pDatabaseIDB As MSSQLServer)
_DatabaseIDB = pDatabaseIDB
End Sub
Public Function GetConnectionString(pConnectionId As Integer) As String
Return _DatabaseECM.GetConnectionStringForId(pConnectionId)
End Function
#Region "GetDatatable"
Public Function GetDatatableECM(pSQL As String) As DataTable
Return GetDatatable(New GetDatatableOptions(pSQL, Constants.DatabaseType.ECM))
End Function
Public Async Function GetDatatableECMAsync(pSQL As String) As Task(Of DataTable)
Return Await Task.Run(Function() GetDatatableECM(pSQL))
End Function
Public Function GetDatatableIDB(pSQL As String) As DataTable
Return GetDatatable(New GetDatatableOptions(pSQL, Constants.DatabaseType.IDB))
End Function
Public Async Function GetDatatableIDBAsync(pSQL As String) As Task(Of DataTable)
Return Await Task.Run(Function() GetDatatableIDB(pSQL))
End Function
Public Function GetDatatableWithConnection(pSQL As String, pConnectionId As Integer) As DataTable
Return GetDatatable(New GetDatatableOptions(pSQL, Constants.DatabaseType.None) With {.ConnectionId = pConnectionId})
End Function
Public Async Function GetDatatableWithConnectionAsync(pSQL As String, pConnectionId As Integer) As Task(Of DataTable)
Return Await Task.Run(Function() GetDatatableWithConnection(pSQL, pConnectionId))
End Function
#End Region
#Region "GetScalarValue"
Public Function GetScalarValueECM(pSQL As String) As Object
Return GetScalarValue(pSQL, Constants.DatabaseType.ECM)
End Function
Public Async Function GetScalarValueECMAsync(pSQL As String) As Task(Of Object)
Return Await Task.Run(Function() GetScalarValueECM(pSQL))
End Function
Public Function GetScalarValueIDB(pSQL As String) As Object
Return GetScalarValue(pSQL, Constants.DatabaseType.IDB)
End Function
Public Async Function GetScalarValueIDBAsync(pSQL As String) As Task(Of Object)
Return Await Task.Run(Function() GetScalarValueIDB(pSQL))
End Function
Public Function GetScalarValueWithConnection(pSQL As String, pConnectionId As Integer) As Object
Return GetScalarValue(pSQL, Constants.DatabaseType.None, pConnectionId:=pConnectionId)
End Function
Public Async Function GetScalarValueWithConnectionAsync(pSQL As String, pConnectionId As Integer) As Task(Of Object)
Return Await Task.Run(Function() GetScalarValueWithConnection(pSQL, pConnectionId))
End Function
#End Region
#Region "ExecuteNonQuery"
Public Function ExecuteNonQueryECM(pSQL As String) As Boolean
Return ExecuteNonQuery(pSQL, Constants.DatabaseType.ECM)
End Function
Public Async Function ExecuteNonQueryECMAsync(pSQL As String) As Task(Of Boolean)
Return Await Task.Run(Function() ExecuteNonQueryECM(pSQL))
End Function
Public Function ExecuteNonQueryIDB(pSQL As String) As Boolean
Return ExecuteNonQuery(pSQL, Constants.DatabaseType.IDB)
End Function
Public Async Function ExecuteNonQueryIDBAsync(pSQL As String) As Task(Of Boolean)
Return Await Task.Run(Function() ExecuteNonQueryIDB(pSQL))
End Function
Public Function ExecuteNonQueryWithConnection(pSQL As String, pConnectionId As Integer) As Boolean
Return ExecuteNonQuery(pSQL, Constants.DatabaseType.None, pConnectionId:=pConnectionId)
End Function
Public Async Function ExecuteNonQueryWithConnectionAsync(pSQL As String, pConnectionId As Integer) As Task(Of Boolean)
Return Await Task.Run(Function() ExecuteNonQueryWithConnection(pSQL, pConnectionId))
End Function
#End Region
'''
''' Returns a Datatable by trying to fetch a cached version from the service, then querying the database through the service and querying the database directly as fallback.
'''
''' Name of the Cached Datatable
''' Options object
Public Function GetDatatable(pDataTableName As String, pOptions As GetDatatableOptions) As DataTable
Return GetDatatable(pDataTableName, pOptions.FallbackSQL, pOptions.FallbackType, pOptions.FilterExpression, pOptions.SortByColumn, pOptions.ForceFallback, pOptions.ConnectionId)
End Function
Public Function GetDatatable(pSQL As String, pConnectionId As Integer) As DataTable
Return GetDatatable("FORCE_FALLBACK", pSQL, Constants.DatabaseType.ECM, pSortByColumn:=Nothing, pForceFallback:=False, pConnectionId:=pConnectionId)
End Function
'''
''' Returns a datatable directly from the database.
'''
''' Options object
Public Function GetDatatable(pOptions As GetDatatableOptions) As DataTable
Dim oForceFallback = True
Return GetDatatable("FORCE_FALLBACK", pOptions.FallbackSQL, pOptions.FallbackType, pOptions.FilterExpression, pOptions.SortByColumn, oForceFallback, pOptions.ConnectionId)
End Function
'''
''' Returns a Datatable by trying to fetch a cached version from the service, then querying the database through the service and querying the database directly as fallback.
'''
''' Name of the Cached Datatable
''' SQL Command to execute as fallback
''' Named Database to use for the fallback SQL Command
''' Filter expression for the cached Datatable
''' Columns to sort the cached Datatable by
''' Force the fallback, skipping the service completely
''' Connection Id to use, references TBDD_CONNECTION
Public Function GetDatatable(pDataTableName As String, pFallbackSQL As String, pFallbackType As Constants.DatabaseType, Optional pFilterExpression As String = "", Optional pSortByColumn As String = "", Optional pForceFallback As Boolean = False, Optional pConnectionId As Integer = 0) As DataTable
Try
Dim oResult As DataTable = Nothing
Dim oTableResult As TableResult = Nothing
' If there is no client, we assume there is no service (configured)
If _Client Is Nothing Then
Return GetDatatableFromDatabase(pFallbackSQL, pFallbackType, pConnectionId)
End If
' If ForceFallback flag is set, we go to database immediately
If pForceFallback Or _ClientConfig.ForceDirectDatabaseAccess Then
Return GetDatatableFromDatabase(pFallbackSQL, pFallbackType, pConnectionId)
End If
' If the table is not cached, we try going through the service
If Not IsTableCached(pDataTableName) Then
Return GetDatatableFromService(pFallbackSQL, pFallbackType, pConnectionId)
End If
' If there is a proper ConnectionId, we try going through the service
If pConnectionId > 0 Then
Return GetDatatableFromService(pFallbackSQL, pFallbackType, pConnectionId)
End If
Try
oTableResult = _Client.GetDatatableByName(pDataTableName, pFilterExpression, pSortByColumn)
Catch ex As Exception
_Logger.Error(ex)
oTableResult = Nothing
End Try
If oTableResult Is Nothing OrElse oTableResult.OK = False Then
_Logger.Warn("Datatable [{0}] could not be fetched from AppServer Cache. Falling back to direct Database Access.", pDataTableName)
Return GetDatatableFromDatabase(pFallbackSQL, pFallbackType, pConnectionId)
Else
Return oTableResult.Table
End If
Catch ex As Exception
_Logger.Error(ex)
Return Nothing
End Try
End Function
'''
''' Returns a Scalar Value by querying the database through the service and querying the database directly as fallback.
'''
''' SQL Command to execute as fallback
''' Named Database to use for the fallback SQL Command
''' Force the fallback, skipping the service completely
''' Connection Id to use, references TBDD_CONNECTION
Public Function GetScalarValue(pSQL As String, pDatabaseType As Constants.DatabaseType, Optional pForceFallback As Boolean = False, Optional pConnectionId As Integer = 0) As Object
Try
' If there is no client, we assume there is no service (configured)
If _Client Is Nothing Then
Return GetScalarValueFromDatabase(pSQL, pDatabaseType, pConnectionId)
End If
' If ForceFallback flag is set, we go to database immediately
If pForceFallback Or _ClientConfig.ForceDirectDatabaseAccess Then
Return GetScalarValueFromDatabase(pSQL, pDatabaseType, pConnectionId)
End If
Return GetScalarValueFromService(pSQL, pDatabaseType, pConnectionId)
Catch ex As Exception
_Logger.Error(ex)
Return Nothing
End Try
End Function
'''
''' Returns a Scalar Value by querying the database through the service and querying the database directly as fallback.
'''
''' SQL Command to execute as fallback
''' Named Database to use for the fallback SQL Command
''' Force the fallback, skipping the service completely
''' Connection Id to use, references TBDD_CONNECTION
Public Function ExecuteNonQuery(pSQL As String, pDatabaseType As Constants.DatabaseType, Optional pForceFallback As Boolean = False, Optional pConnectionId As Integer = 0) As Boolean
Try
Dim oResult As DataTable = Nothing
Dim oTableResult As TableResult = Nothing
' If there is no client, we assume there is no service (configured)
If _Client Is Nothing Then
Return ExecuteNonQueryFromDatabase(pSQL, pDatabaseType, pConnectionId)
End If
' If ForceFallback flag is set, we go to database immediately
If pForceFallback Or _ClientConfig.ForceDirectDatabaseAccess Then
Return ExecuteNonQueryFromDatabase(pSQL, pDatabaseType, pConnectionId)
End If
Return ExecuteNonQueryFromService(pSQL, pDatabaseType, pConnectionId)
Catch ex As Exception
_Logger.Error(ex)
Return Nothing
End Try
End Function
Private Function IsTableCached(pName As String) As Boolean
If _Client Is Nothing Then
Return False
End If
Return _Client.CachedTables.Contains(pName.ToUpper)
End Function
Private Function GetDatatableFromService(pSQLCommand As String, DatabaseType As Constants.DatabaseType, pConnectionId As Integer) As DataTable
Try
Dim oResult As GetDatatableResponse = Nothing
Select Case DatabaseType
Case Constants.DatabaseType.ECM
oResult = _Client.GetDatatableFromECM(pSQLCommand)
Case Constants.DatabaseType.IDB
oResult = _Client.GetDatatableFromIDB(pSQLCommand)
Case Else
Return GetDatatableFromDatabase(pSQLCommand, DatabaseType, pConnectionId)
End Select
If oResult Is Nothing Then
Throw New ApplicationException("Unexpected server error ocurred!")
End If
If oResult.OK = False Then
Throw New ApplicationException(oResult.ErrorMessage)
End If
Return oResult.Table
Catch ex As Exception
_Logger.Warn("GetDatatableFromService failed. Falling back to direct database access.")
_Logger.Error(ex)
Return GetDatatableFromDatabase(pSQLCommand, DatabaseType, pConnectionId)
End Try
End Function
Private Function GetDatatableFromDatabase(pSQLCommand As String, DatabaseType As Constants.DatabaseType, pConnectionId As Integer) As DataTable
Try
Select Case DatabaseType
Case Constants.DatabaseType.IDB
Return _DatabaseIDB.GetDatatable(pSQLCommand)
Case Else
If pConnectionId > 0 Then
Dim oConnectionString = _DatabaseECM.Get_ConnectionStringforID(pConnectionId)
Return _DatabaseECM.GetDatatableWithConnection(pSQLCommand, oConnectionString)
Else
Return _DatabaseECM.GetDatatable(pSQLCommand)
End If
End Select
Catch ex As Exception
_Logger.Error(ex)
Return Nothing
End Try
End Function
Private Function GetScalarValueFromService(pSQLCommand As String, DatabaseType As Constants.DatabaseType, pConnectionId As Integer) As Object
Try
Dim oResult As GetScalarValueResponse = Nothing
Select Case DatabaseType
Case Constants.DatabaseType.ECM
oResult = _Client.GetScalarValueFromECM(pSQLCommand)
Case Constants.DatabaseType.IDB
oResult = _Client.GetScalarValueFromIDB(pSQLCommand)
Case Else
Return GetScalarValueFromDatabase(pSQLCommand, DatabaseType, pConnectionId)
End Select
If oResult Is Nothing Then
Throw New ApplicationException("Unexpected server error ocurred!")
End If
If oResult.OK = False Then
Throw New ApplicationException(oResult.ErrorMessage)
End If
Return oResult.Scalar
Catch ex As Exception
_Logger.Warn("GetScalarValueFromService failed. Falling back to direct database access.")
_Logger.Error(ex)
Return GetScalarValueFromDatabase(pSQLCommand, DatabaseType, pConnectionId)
End Try
End Function
Private Function GetScalarValueFromDatabase(pSQLCommand As String, DatabaseType As Constants.DatabaseType, pConnectionId As Integer) As Object
Try
Select Case DatabaseType
Case Constants.DatabaseType.IDB
Return _DatabaseIDB.GetScalarValue(pSQLCommand)
Case Else
If pConnectionId > 0 Then
Dim oConnectionString = _DatabaseECM.Get_ConnectionStringforID(pConnectionId)
Return _DatabaseECM.GetScalarValueWithConnection(pSQLCommand, oConnectionString)
Else
Return _DatabaseECM.GetScalarValue(pSQLCommand)
End If
End Select
Catch ex As Exception
_Logger.Error(ex)
Return Nothing
End Try
End Function
Private Function ExecuteNonQueryFromService(pSQLCommand As String, DatabaseType As Constants.DatabaseType, pConnectionId As Integer) As Boolean
Try
Dim oResult As ExecuteNonQueryResponse = Nothing
Select Case DatabaseType
Case Constants.DatabaseType.ECM
oResult = _Client.ExecuteNonQueryFromECM(pSQLCommand)
Case Constants.DatabaseType.IDB
oResult = _Client.ExecuteNonQueryFromIDB(pSQLCommand)
Case Else
Return ExecuteNonQueryFromDatabase(pSQLCommand, DatabaseType, pConnectionId)
End Select
If oResult Is Nothing Then
Throw New ApplicationException("Unexpected server error ocurred!")
End If
If oResult.OK = False Then
Throw New ApplicationException(oResult.ErrorMessage)
End If
Return oResult.Result
Catch ex As Exception
_Logger.Warn("ExecuteNonQueryFromService failed. Falling back to direct database access.")
_Logger.Error(ex)
Return ExecuteNonQueryFromDatabase(pSQLCommand, DatabaseType, pConnectionId)
End Try
End Function
Private Function ExecuteNonQueryFromDatabase(pSQLCommand As String, DatabaseType As Constants.DatabaseType, pConnectionId As Integer) As Boolean
Try
Select Case DatabaseType
Case Constants.DatabaseType.IDB
Return _DatabaseIDB.ExecuteNonQuery(pSQLCommand)
Case Else
If pConnectionId > 0 Then
Dim oConnectionString = _DatabaseECM.Get_ConnectionStringforID(pConnectionId)
Return _DatabaseECM.ExecuteNonQueryWithConnection(pSQLCommand, oConnectionString)
Else
Return _DatabaseECM.ExecuteNonQuery(pSQLCommand)
End If
End Select
Catch ex As Exception
_Logger.Error(ex)
Return False
End Try
End Function
End Class