Imports FirebirdSql.Data.FirebirdClient Imports System.Text.RegularExpressions Imports DigitalData.Modules.Logging Imports System.ComponentModel ''' ''' MODULE: Firebird ''' ''' VERSION: 0.0.0.4 ''' ''' DATE: 18.12.2018 ''' ''' DESCRIPTION: ''' ''' DEPENDENCIES: NLog, >= 4.5.10 ''' ''' EntityFramework.Firebird, >= 6.4.0 ''' ''' FirebirdSql.Data.FirebirdClient, >= 6.4.0 ''' ''' PARAMETERS: LogConfig, DigitalData.Modules.Logging.LogConfig ''' The LogFactory containing the current log config. Used to instanciate the class logger for this and any dependent class ''' ''' DataSource, String ''' The server where the database lives, for example 127.0.0.1 or dd-vmx09-vm03 ''' ''' Database, String ''' The location of the Database in the format `127.0.0.1:E:\Path\To\Database.FDB` ''' ''' User, String ''' The user name to connect as ''' ''' Password, String ''' The user's password ''' ''' PROPERTIES: ConnectionEstablished, Boolean ''' If the last opened connection was successful ''' ''' ConnectionFailed, Boolean ''' If the last opened connection failed ''' ''' ConnectionString, String ''' The used connectionstring ''' ''' EXAMPLES: ''' ''' REMARKS: If the connection fails due to "wrong username or password", the cause might be that the server harddrive is full.. ''' Public Class Firebird Private _Logger As Logger Private _LogConfig As LogConfig Private _connectionServer As String Private _connectionDatabase As String Private _connectionUsername As String Private _connectionPassword As String Private _connectionString As String Public _DBInitialized As Boolean = False Public Const MAX_POOL_SIZE = 1000 Public Enum TransactionMode NoTransaction ExternalTransaction WithTransaction End Enum Public ReadOnly Property ConnectionString As String Get Return _connectionString End Get End Property Public ReadOnly Property DatabaseName As String Get Dim oRegex As New Regex("^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:") Dim oPath As String = oRegex.Replace(_connectionDatabase, "") Dim oFileInfo As New IO.FileInfo(oPath) Return oFileInfo.Name End Get End Property ''' ''' ''' ''' The LogFactory containing the current log config. Used to instanciate the class logger for this and any dependent class ''' The server where the database lives, for example 127.0.0.1 or dd-vmx09-vm03 ''' The location of the Database in the format `127.0.0.1:E:\Path\To\Database.FDB` ''' The user name to connect as ''' The user's password ''' Public Sub New(LogConfig As LogConfig, Datasource As String, Database As String, User As String, Password As String) Try _LogConfig = LogConfig _Logger = _LogConfig.GetLogger() Dim oConnectionString = GetConnectionString(Datasource, Database, User, Password) _connectionServer = Datasource _connectionDatabase = Database _connectionUsername = User _connectionPassword = Password _connectionString = oConnectionString _Logger.Debug("Connecting to database..") ' Test the connection Dim oConnection = GetConnection() ' If initial connection was successfully, close it oConnection.Close() If oConnection Is Nothing Then Throw New Exceptions.DatabaseException() Else _DBInitialized = True End If _Logger.Debug("Connection sucessfully established!") Catch ex As Exception _Logger.Error(ex) End Try End Sub Public Function GetConnection() As FbConnection Try Dim oConnection = New FbConnection(_connectionString) oConnection.Open() Return oConnection Catch ex As Exception _Logger.Error(ex) Return Nothing End Try End Function ''' ''' Builds a connectionstring from the provided arguments. ''' ''' The database server where to connect to ''' The datasource, eg. the path of the FDB-file ''' The user used to connect to the database ''' The password of the connecting user ''' A connectionstring Private Function GetConnectionString(DataSource As String, Database As String, User As String, Password As String) As String Return New FbConnectionStringBuilder With { .DataSource = DataSource, .Database = Database, .UserID = User, .Password = Password, .Charset = "UTF8", .MaxPoolSize = MAX_POOL_SIZE }.ToString() End Function Private Function MaybeGetTransaction(Connection As FbConnection, Mode As TransactionMode, Transaction As FbTransaction) As FbTransaction If Mode = TransactionMode.NoTransaction Then Return Nothing ElseIf Mode = TransactionMode.ExternalTransaction Then Return Transaction Else Return Connection.BeginTransaction() End If End Function Private Function MaybeCommitTransaction(Transaction As FbTransaction, TransactionMode As TransactionMode) As Boolean Select Case TransactionMode Case TransactionMode.NoTransaction Return True Case TransactionMode.ExternalTransaction Return True Case TransactionMode.WithTransaction Try Transaction.Commit() Return True Catch ex As Exception _Logger.Error(ex) Return False End Try Case Else Return True End Select End Function ''' ''' Executes a non-query command. ''' ''' The command to execute ''' The Firebird connection to use ''' True, if command was executed sucessfully. Otherwise false. Public Function ExecuteNonQueryWithConnection(SqlCommand As String, Connection As FbConnection, Optional TransactionMode As TransactionMode = TransactionMode.WithTransaction, Optional Transaction As FbTransaction = Nothing) As Boolean _Logger.Debug("Executing Non-Query: {0}", SqlCommand) If Connection Is Nothing Then _Logger.Warn("Connection is nothing!") Return Nothing End If Dim oTransaction = MaybeGetTransaction(Connection, TransactionMode, Transaction) Try Dim oCommand As New FbCommand With { .CommandText = SqlCommand, .Connection = Connection } If Not IsNothing(oTransaction) Then oCommand.Transaction = oTransaction End If oCommand.ExecuteNonQuery() _Logger.Debug("Command executed!") Catch ex As Exception _Logger.Error(ex, $"Error in ExecuteNonQuery while executing command: [{SqlCommand}]") _Logger.Warn($"Unexpected error in ExecuteNonQueryWithConnection: [{SqlCommand}]") Throw ex Finally MaybeCommitTransaction(oTransaction, TransactionMode) End Try Return True End Function ''' ''' Executes a non-query command. ''' ''' The command to execute ''' True, if command was executed sucessfully. Otherwise false. Public Function ExecuteNonQuery(SqlCommand As String) As Boolean Using oConnection As FbConnection = GetConnection() Return ExecuteNonQueryWithConnection(SqlCommand, oConnection) End Using End Function ''' ''' Executes a non-query command inside the specified transaction. ''' ''' The command to execute ''' True, if command was executed sucessfully. Otherwise false. Public Function ExecuteNonQuery(SqlCommand As String, Transaction As FbTransaction) As Boolean Using oConnection As FbConnection = GetConnection() Return ExecuteNonQueryWithConnection(SqlCommand, oConnection, TransactionMode.ExternalTransaction, Transaction) End Using End Function ''' ''' Executes a sql query resulting in a scalar value. ''' ''' The query to execute ''' The Firebird connection to use ''' The scalar value if the command was executed successfully. Nothing otherwise. Public Function GetScalarValueWithConnection(SqlQuery As String, Connection As FbConnection, Optional TransactionMode As TransactionMode = TransactionMode.WithTransaction, Optional Transaction As FbTransaction = Nothing) As Object _Logger.Debug("Fetching Scalar-Value: {0}", SqlQuery) If Connection Is Nothing Then _Logger.Warn("Connection is nothing!") Return Nothing End If Dim oTransaction = MaybeGetTransaction(Connection, TransactionMode, Transaction) Dim oResult As Object Try Dim oCommand As New FbCommand With { .CommandText = SqlQuery, .Connection = Connection, .Transaction = oTransaction } oResult = oCommand.ExecuteScalar() Catch ex As Exception _Logger.Error(ex, $"Error in ReturnScalar while executing command: [{SqlQuery}]") Throw ex Finally MaybeCommitTransaction(oTransaction, TransactionMode) End Try Return oResult End Function ''' ''' Executes a sql query resulting in a scalar value. ''' ''' The query to execute ''' The scalar value if the command was executed successfully. Nothing otherwise. Public Function GetScalarValue(SqlQuery As String) As Object Dim oConnection As FbConnection = GetConnection() Dim oScalarValue As Object = GetScalarValueWithConnection(SqlQuery, oConnection) oConnection.Close() Return oScalarValue End Function ''' ''' Executes a sql query resulting in a table of values. ''' ''' The query to execute ''' The Firebird connection to use ''' A datatable containing the results if the command was executed successfully. Nothing otherwise. Public Function GetDatatableWithConnection(SqlQuery As String, Connection As FbConnection, Optional TransactionMode As TransactionMode = TransactionMode.NoTransaction, Optional Transaction As FbTransaction = Nothing) As DataTable _Logger.Debug("Fetching Datatable: {0}", SqlQuery) If Connection Is Nothing Then _Logger.Warn("Connection is nothing!") Return Nothing End If Dim oTransaction = MaybeGetTransaction(Connection, TransactionMode, Transaction) Dim oDatatable As New DataTable() With { .TableName = "DDRESULT" } Try Dim oAdapter As New FbDataAdapter(New FbCommand With { .CommandText = SqlQuery, .Connection = Connection, .Transaction = oTransaction }) oAdapter.Fill(oDatatable) Catch ex As Exception _Logger.Error(ex) _Logger.Warn("Error in GetDatatableWithConnection while executing command: [{0}]", SqlQuery) Throw ex Finally MaybeCommitTransaction(oTransaction, TransactionMode) End Try Return oDatatable End Function ''' ''' Executes a sql query resulting in a table of values. ''' ''' The query to execute ''' A datatable containing the results if the command was executed successfully. Nothing otherwise. Public Function GetDatatable(SqlQuery As String, Optional TransactionMode As TransactionMode = TransactionMode.NoTransaction, Optional Transaction As FbTransaction = Nothing) As DataTable Try Dim oConnection As FbConnection = GetConnection() Dim oDatatable As DataTable = GetDatatableWithConnection(SqlQuery, oConnection, TransactionMode, Transaction) oConnection.Close() Return oDatatable Catch ex As Exception _Logger.Error(ex) _Logger.Warn("Error in GetDatatable while executing command: '{0}'", SqlQuery) Throw ex End Try End Function End Class