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 Dim oResult As ExecuteNonQueryResponse = Nothing Select Case DatabaseType Case Constants.DatabaseType.ECM Return _DatabaseECM.GetDatatable(pSQLCommand) Case Constants.DatabaseType.IDB Return _DatabaseIDB.GetDatatable(pSQLCommand) Case Else Dim oConnectionString = _DatabaseECM.Get_ConnectionStringforID(pConnectionId) Return _DatabaseECM.GetDatatableWithConnection(pSQLCommand, oConnectionString) 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.ECM Return _DatabaseECM.GetScalarValue(pSQLCommand) Case Constants.DatabaseType.IDB Return _DatabaseIDB.GetScalarValue(pSQLCommand) Case Else Dim oConnectionString = _DatabaseECM.Get_ConnectionStringforID(pConnectionId) Return _DatabaseECM.GetScalarValueWithConnection(pSQLCommand, oConnectionString) 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.ECM Return _DatabaseECM.ExecuteNonQuery(pSQLCommand) Case Constants.DatabaseType.IDB Return _DatabaseIDB.ExecuteNonQuery(pSQLCommand) Case Else Dim oConnectionString = _DatabaseECM.Get_ConnectionStringforID(pConnectionId) Return _DatabaseECM.ExecuteNonQueryWithConnection(pSQLCommand, oConnectionString) End Select Catch ex As Exception _Logger.Error(ex) Return False End Try End Function End Class