From 37609ee918260dc69de19be8b12f45e2fad2474d Mon Sep 17 00:00:00 2001 From: Jonathan Jenne Date: Thu, 15 Apr 2021 11:13:46 +0200 Subject: [PATCH] Database: Add Support for MSSQL Transactions BREAKING CHANGE: Rename GetScalarValueConStr and ExecuteNonQueryCS --- Modules.Database/MSSQLServer.vb | 381 ++++++++++++++++---------------- 1 file changed, 185 insertions(+), 196 deletions(-) diff --git a/Modules.Database/MSSQLServer.vb b/Modules.Database/MSSQLServer.vb index 59f22200..0d850307 100644 --- a/Modules.Database/MSSQLServer.vb +++ b/Modules.Database/MSSQLServer.vb @@ -1,4 +1,5 @@ -Imports System.Data.Common +Imports System.ComponentModel +Imports System.Data.Common Imports System.Data.SqlClient Imports DigitalData.Modules.Logging @@ -8,9 +9,21 @@ Public Class MSSQLServer Public DBInitialized As Boolean = False Public CurrentSQLConnectionString As String = "" + Public Const TIMEOUT_DEFAULT As Integer = 120 + Public Const TABLE_DEFAULT As String = "DDRESULT" + Private ReadOnly _Timeout As Integer Private ReadOnly _Logger As Logger + Public Enum TransactionMode + + NoTransaction + + ExternalTransaction + + WithTransaction + End Enum + Public Sub New(LogConfig As LogConfig, ConnectionString As String, Optional Timeout As Integer = 120) _Logger = LogConfig.GetLogger() _Timeout = Timeout @@ -49,6 +62,36 @@ Public Class MSSQLServer Return oConnectionStringBuilder.ToString End Function + + Private Function MaybeGetTransaction(Connection As SqlConnection, Mode As TransactionMode, Transaction As SqlTransaction) As SqlTransaction + 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 SqlTransaction, 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 + Public Function Get_ConnectionStringforID(pConnectionId As Integer) Dim connectionString As String = "" Try @@ -94,7 +137,7 @@ Public Class MSSQLServer Private Function TestCanConnect(ConnectionString As String) As Boolean Try - _Logger.Debug("Testing connection to [{0}]", ConnectionString) + _Logger.Debug("Testing connection to [{0}]", MaskConnectionString(ConnectionString)) Dim oConnection As New SqlConnection(ConnectionString) oConnection.Open() oConnection.Close() @@ -105,11 +148,28 @@ Public Class MSSQLServer End Try End Function + Private Function TestCanConnect(Connection As SqlConnection) As Boolean + Try + If Connection Is Nothing Then + _Logger.Warn("TestCanConnect: Connection is nothing!") + Return False + End If + + _Logger.Debug("Testing connection to [{0}]", MaskConnectionString(Connection.ConnectionString)) + Connection.Open() + Connection.Close() + Return True + Catch ex As Exception + _Logger.Error(ex) + Return False + End Try + End Function + Private Function GetSQLConnection() As SqlConnection - Return GetSQLConnection(CurrentSQLConnectionString) + Return GetConnection(CurrentSQLConnectionString) End Function - Private Function GetSQLConnection(ConnectionString As String) As SqlConnection + Private Function GetConnection(ConnectionString As String) As SqlConnection Try Dim oConnection As New SqlConnection(ConnectionString) oConnection.Open() @@ -136,233 +196,186 @@ Public Class MSSQLServer End Try End Function + Public Function GetDatatable(SqlCommand As String) As DataTable Implements IDatabase.GetDatatable + Return GetDatatable(SqlCommand, _Timeout) + End Function + ''' ''' Returns a datatable for a sql-statement ''' ''' sqlcommand for datatable (select XYZ from TableORView) ''' Returns a datatable Public Function GetDatatable(SqlCommand As String, Timeout As Integer) As DataTable Implements IDatabase.GetDatatable - Try - If TestCanConnect() = False Then - Return Nothing - End If - - _Logger.Debug("GetDatatable: Running Query [{0}]", SqlCommand) - - Using oConnection = GetSQLConnection() - Using oSQLCOmmand = oConnection.CreateCommand() - oSQLCOmmand.CommandText = SqlCommand - oSQLCOmmand.CommandTimeout = Timeout - - Dim dt As DataTable = New DataTable() - Dim oAdapter As SqlDataAdapter = New SqlDataAdapter(oSQLCOmmand) - oAdapter.Fill(dt) - Return dt - End Using - End Using - Catch ex As Exception - _Logger.Error(ex) - _Logger.Warn($"GetDatatable failed SQLCommand [{SqlCommand}]") - Return Nothing - End Try + Using oSqlConnection = GetSQLConnection() + Return GetDatatableWithConnectionObject(SqlCommand, oSqlConnection, TransactionMode.WithTransaction, Nothing, Timeout) + End Using End Function - Public Function GetDatatable(SqlCommand As String) As DataTable Implements IDatabase.GetDatatable - Return GetDatatable(SqlCommand, _Timeout) + Public Function GetDatatable(SqlCommand As String, Transaction As SqlTransaction) As DataTable + Using oSqlConnection = GetSQLConnection() + Return GetDatatableWithConnectionObject(SqlCommand, oSqlConnection, TransactionMode.ExternalTransaction, Transaction) + End Using End Function + Public Async Function GetDatatableAsync(SqlCommand As String) As Task(Of DataTable) - Return Await Task.Run(Function() - Return GetDatatable(SqlCommand, _Timeout) - End Function) + Return Await Task.Run(Function() GetDatatable(SqlCommand, _Timeout)) End Function Public Function GetDatatableWithConnection(SqlCommand As String, ConnectionString As String) As DataTable - Try - If TestCanConnect(ConnectionString) = False Then - Return Nothing - End If - - _Logger.Debug("GetDatatableWithConnection: Running Query [{0}]", SqlCommand) - - Using oConnection = GetSQLConnection(ConnectionString) - Using oSQLCOmmand = oConnection.CreateCommand() - oSQLCOmmand.CommandText = SqlCommand - oSQLCOmmand.CommandTimeout = _Timeout + Using oConnection = GetConnection(ConnectionString) + Return GetDatatableWithConnectionObject(SqlCommand, oConnection) + End Using + End Function - Dim dt As DataTable = New DataTable() - Dim oAdapter As SqlDataAdapter = New SqlDataAdapter(oSQLCOmmand) - oAdapter.Fill(dt) - Return dt - End Using - End Using - Catch ex As Exception - _Logger.Error(ex) - _Logger.Warn($"GetDatatableWithConnection failed with SQLCommand [{SqlCommand}] and ConnectionString [{MaskConnectionString(ConnectionString)}]") + Public Function GetDatatableWithConnectionObject(SqlCommand As String, SqlConnection As SqlConnection, + Optional TransactionMode As TransactionMode = TransactionMode.WithTransaction, + Optional Transaction As SqlTransaction = Nothing, + Optional Timeout As Integer = 120) As DataTable + If TestCanConnect(SqlConnection) = False Then Return Nothing - End Try - End Function + End If + + Dim oTransaction As SqlTransaction = MaybeGetTransaction(SqlConnection, TransactionMode, Transaction) + Dim oTable As New DataTable() With {.TableName = TABLE_DEFAULT} - Public Function GetDatatableWithConnection2(SqlCommand As String, ConnectionString As String) As DataTable Try - If TestCanConnect(ConnectionString) = False Then - Return Nothing - End If + Dim oAdapter As New SqlDataAdapter(New SqlCommand With { + .CommandText = SqlCommand, + .Connection = SqlConnection, + .Transaction = oTransaction, + .CommandTimeout = Timeout + }) - _Logger.Debug("GetDatatableWithConnection2: Running Query [{0}]", SqlCommand) + _Logger.Debug("GetDatatableWithConnectionObject: Running Query [{0}]", SqlCommand) - Using oConnection = GetSQLConnection(ConnectionString) - Using oSQLCOmmand = oConnection.CreateCommand() - oSQLCOmmand.CommandText = SqlCommand - oSQLCOmmand.CommandTimeout = _Timeout + oAdapter.Fill(oTable) - Dim oTable As DataTable = New DataTable("DD_RESULT") + _Logger.Debug("GetDatatableWithConnectionObject: Running Query [{0}]", SqlCommand) + Catch ex As Exception + _Logger.Error(ex) + _Logger.Warn("GetDatatableWithConnectionObject: Error in GetDatatableWithConnection while executing command: [{0}]", SqlCommand) + Throw ex + Finally + MaybeCommitTransaction(oTransaction, TransactionMode) + End Try - Using oReader As SqlDataReader = oSQLCOmmand.ExecuteReader() - Dim oSchemaTable As DataTable = oReader.GetSchemaTable() + Return oTable + End Function - For Each oRow As DataRow In oSchemaTable.Rows - Dim oDataColumn As New DataColumn - oDataColumn.ColumnName = oRow("ColumnName").ToString() - oDataColumn.DataType = Type.GetType(oRow("DataType").ToString()) - oTable.Columns.Add(oDataColumn) - Next + Public Function ExecuteNonQuery(SQLCommand As String) As Boolean Implements IDatabase.ExecuteNonQuery + Using oConnection = GetSQLConnection() + Return ExecuteNonQueryWithConnectionObject(SQLCommand, oConnection, TransactionMode.WithTransaction, Nothing, TIMEOUT_DEFAULT) + End Using + End Function - While (oReader.Read()) - Dim oRow As DataRow = oTable.NewRow() + Public Function ExecuteNonQuery(SQLCommand As String, Timeout As Integer) As Boolean Implements IDatabase.ExecuteNonQuery + Using oConnection = GetSQLConnection() + Return ExecuteNonQueryWithConnectionObject(SQLCommand, oConnection, TransactionMode.WithTransaction, Nothing, TIMEOUT_DEFAULT) + End Using + End Function - For index = 0 To oTable.Columns.Count - 1 - oRow.Item(index) = oReader.Item(index) - Next - oTable.Rows.Add(oRow) - End While + Public Function ExecuteNonQuery(SQLCommand As String, Transaction As SqlTransaction) As Boolean + Using oConnection = GetSQLConnection() + Return ExecuteNonQueryWithConnectionObject(SQLCommand, oConnection, TransactionMode.ExternalTransaction, Transaction, TIMEOUT_DEFAULT) + End Using + End Function - Return oTable - End Using - End Using - End Using - Catch ex As Exception - _Logger.Error(ex) - _Logger.Warn($"GetDatatableWithConnection2 failed SQLCommand [{SqlCommand}]") + Public Async Function ExecuteNonQueryAsync(SQLCommand As String) As Task(Of Boolean) + Return Await Task.Run(Function() ExecuteNonQuery(SQLCommand)) + End Function - Return Nothing - End Try + Public Function ExecuteNonQueryWithConnection(pSQLCommand As String, ConnString As String) As Boolean + Using oConnection = GetConnection(ConnString) + Return ExecuteNonQueryWithConnectionObject(pSQLCommand, oConnection) + End Using End Function - Public Function ExecuteNonQuery(SQLCommand As String, Timeout As Integer) As Boolean Implements IDatabase.ExecuteNonQuery - Try - If TestCanConnect() = False Then - Return Nothing - End If + Public Function ExecuteNonQueryWithConnectionObject(SqlCommand As String, SqlConnection As SqlConnection, + Optional TransactionMode As TransactionMode = TransactionMode.WithTransaction, + Optional Transaction As SqlTransaction = Nothing, + Optional Timeout As Integer = 120) As Boolean + If TestCanConnect(SqlConnection) = False Then + Return False + End If - _Logger.Debug("ExecuteNonQuery: Running Query [{0}]", SQLCommand) + Dim oTransaction As SqlTransaction = MaybeGetTransaction(SqlConnection, TransactionMode, Transaction) - Using oConnection = GetSQLConnection() - Using oSQLCOmmand = oConnection.CreateCommand() - oSQLCOmmand.CommandText = SQLCommand - oSQLCOmmand.CommandTimeout = Timeout - oSQLCOmmand.ExecuteNonQuery() - Return True - End Using + Try + Using oSQLCOmmand = SqlConnection.CreateCommand() + oSQLCOmmand.CommandText = SqlCommand + oSQLCOmmand.CommandTimeout = Timeout + oSQLCOmmand.Transaction = oTransaction + oSQLCOmmand.ExecuteNonQuery() End Using - Catch ex As Exception - _Logger.Error(ex) - _Logger.Warn($"ExecuteNonQuery failed SQLCommand [{SQLCommand}]") - Return False - End Try - End Function - Public Function ExecuteNonQueryCS(pSQLCommand As String, ConnString As String, Optional pInfo As String = "") - Try - If pInfo <> "" Then - pInfo = "[" & pInfo & "]" - End If - Dim SQLconnect As New SqlClient.SqlConnection - Dim SQLcommand As SqlClient.SqlCommand - SQLconnect.ConnectionString = ConnString - SQLconnect.Open() - SQLcommand = SQLconnect.CreateCommand - 'Update Last Created Record in Foo - SQLcommand.CommandText = pSQLCommand - _Logger.Debug("Execute_non_Query_ConStr Created: " & pSQLCommand) - SQLcommand.ExecuteNonQuery() - SQLcommand.Dispose() - SQLconnect.Close() Return True Catch ex As Exception _Logger.Error(ex) - _Logger.Warn($"ExecuteNonQueryCS failed SQLCommand [{pSQLCommand}]") - + _Logger.Warn("GetDatatableWithConnectionObject: Error in GetDatatableWithConnection while executing command: [{0}]", SqlCommand) Return False + Finally + MaybeCommitTransaction(oTransaction, TransactionMode) End Try End Function - Public Function ExecuteNonQuery(SQLCommand As String) As Boolean Implements IDatabase.ExecuteNonQuery - Return ExecuteNonQuery(SQLCommand, _Timeout) - End Function - Public Async Function ExecuteNonQueryAsync(SQLCommand As String) As Task(Of Boolean) - Return Await Task.Run(Function() - Return ExecuteNonQuery(SQLCommand, _Timeout) - End Function) + Public Function GetScalarValue(SQLQuery As String) As Object Implements IDatabase.GetScalarValue + Using oConnection = GetSQLConnection() + Return GetScalarValueWithConnectionObject(SQLQuery, oConnection) + End Using End Function Public Function GetScalarValue(SQLCommand As String, Timeout As Integer) As Object Implements IDatabase.GetScalarValue - Try - If TestCanConnect() = False Then - Return Nothing - End If - - _Logger.Debug("GetScalarValue: Running Query [{0}]", SQLCommand) - - Using oConnection As SqlConnection = GetSQLConnection() - Using oSQLCOmmand = oConnection.CreateCommand() - oSQLCOmmand.CommandText = SQLCommand - oSQLCOmmand.CommandTimeout = Timeout - Dim oResult As Object = oSQLCOmmand.ExecuteScalar() - Return oResult - End Using - End Using - Catch ex As Exception - _Logger.Error(ex) - _Logger.Warn($"GetScalarValue failed SQLCommand [{SQLCommand}]") - - Return Nothing - End Try + Using oConnection = GetSQLConnection() + Return GetScalarValueWithConnectionObject(SQLCommand, oConnection, TransactionMode.WithTransaction, Nothing, Timeout) + End Using End Function - Public Function GetScalarValue(SQLQuery As String) As Object Implements IDatabase.GetScalarValue - Return GetScalarValue(SQLQuery, _Timeout) + Public Function GetScalarValue(SQLCommand As String, Transaction As SqlTransaction) As Object + Using oConnection = GetSQLConnection() + Return GetScalarValueWithConnectionObject(SQLCommand, oConnection, TransactionMode.ExternalTransaction, Transaction) + End Using End Function Public Async Function GetScalarValueAsync(SQLQuery As String) As Task(Of Object) - Return Await Task.Run(Function() - Return GetScalarValue(SQLQuery, _Timeout) - End Function) + Return Await Task.Run(Function() GetScalarValue(SQLQuery, _Timeout)) End Function Public Function GetScalarValueWithConnection(SQLCommand As String, ConnectionString As String) As Object - Try - If TestCanConnect(ConnectionString) = False Then - Return Nothing - End If + Using oConnection = GetConnection(ConnectionString) + Return GetScalarValueWithConnectionObject(SQLCommand, oConnection) + End Using + End Function - _Logger.Debug("GetScalarValue: Running Query [{0}]", SQLCommand) + Public Function GetScalarValueWithConnectionObject(SqlCommand As String, SqlConnection As SqlConnection, + Optional TransactionMode As TransactionMode = TransactionMode.WithTransaction, + Optional Transaction As SqlTransaction = Nothing, + Optional Timeout As Integer = 120) As Object + If TestCanConnect() = False Then + Return Nothing + End If - Using oConnection As SqlConnection = GetSQLConnection(ConnectionString) - Using oSQLCOmmand = oConnection.CreateCommand() - oSQLCOmmand.CommandText = SQLCommand - oSQLCOmmand.CommandTimeout = _Timeout - Dim oResult As Object = oSQLCOmmand.ExecuteScalar() - Return oResult - End Using + Dim oTransaction As SqlTransaction = MaybeGetTransaction(SqlConnection, TransactionMode, Transaction) + Dim oResult As Object = Nothing + + Try + Using oSQLCOmmand = SqlConnection.CreateCommand() + oSQLCOmmand.CommandText = SqlCommand + oSQLCOmmand.CommandTimeout = Timeout + oSQLCOmmand.Transaction = oTransaction + + oResult = oSQLCOmmand.ExecuteScalar() End Using Catch ex As Exception _Logger.Error(ex) - _Logger.Warn($"GetScalarValueWithConnection failed SQLCommand [{SQLCommand}]") - Return Nothing + _Logger.Warn("GetDatatableWithConnectionObject: Error in GetDatatableWithConnection while executing command: [{0}]", SqlCommand) + Finally + MaybeCommitTransaction(oTransaction, TransactionMode) End Try + + Return oResult End Function Public Function GetScalarValue(SQLCommand As SqlCommand, OutputParameter As String, Timeout As Integer) As Object @@ -396,30 +409,6 @@ Public Class MSSQLServer Return Nothing End Try End Function - Public Function GetScalarValueConStr(pSQLCommand As String, ConString As String, Optional pInfo As String = "") - Dim result - Try - If pInfo <> "" Then - pInfo = "[" & pInfo & "]" - End If - Dim SQLconnect As New SqlClient.SqlConnection - Dim SQLcommand As SqlClient.SqlCommand - SQLconnect.ConnectionString = ConString - SQLconnect.Open() - SQLcommand = SQLconnect.CreateCommand - 'Update Last Created Record in Foo - SQLcommand.CommandText = pSQLCommand - _Logger.Debug("Execute_Scalar_ConStr Scalar: " & pSQLCommand) - result = SQLcommand.ExecuteScalar() - SQLcommand.Dispose() - SQLconnect.Close() - Return result - Catch ex As Exception - _Logger.Error(ex) - _Logger.Warn($"GetScalarValueConStr failed SQLCommand [{pSQLCommand}]") - Return Nothing - End Try - End Function Public Function GetScalarValue(SQLCommand As SqlCommand, OutputParameter As String) As Object Return GetScalarValue(SQLCommand, OutputParameter, _Timeout)