Verbesserung von Stabilität und Fehlerbehandlung

- Implementierung von `IDisposable` in der Klasse `Client`, um Ressourcen wie Timer und Kanäle ordnungsgemäß freizugeben.
- Einführung von `ActiveClients` und `ClientStatus` zur Verfolgung und Statusanzeige aktiver Clients.
- Verbesserte Fehlerbehandlung mit der Methode `HandleCommunicationError`, die Kommunikationsfehler protokolliert, den Client offline setzt und einen Reconnect auslöst.
- Optimierung der Reconnect-Logik mit asynchronen Wiederverbindungsversuchen und gestaffelten Verzögerungen.
- Erweiterung der Konstruktoren in `Client` um einen optionalen `pClientName`-Parameter und Generierung eines eindeutigen `ClientId`.
- Verbesserte Timer-Logik und asynchrone Verarbeitung in `UpdateTimer_Elapsed`.
- Einführung der Methode `EnsureAvailable`, um sicherzustellen, dass der Kanal vor Service-Aufrufen verfügbar ist.
- Hinzufügen asynchroner Varianten für Service-Methoden (`GetDatatable`, `GetScalarValue`, `ExecuteNonQuery`) und Fallback auf direkte Datenbankzugriffe.
- Verbesserte Timeout-Einstellungen und Verbindungsverwaltung in der Klasse `Channel`.
- Erweiterungen in `DatabaseWithFallback` für asynchrone Methoden und detaillierteres Logging.
- Aktualisierung der Assembly-Version auf `2.7.0`.
This commit is contained in:
Developer01
2026-09-22 16:36:20 +02:00
parent 857e1366fe
commit ed655879f9
5 changed files with 603 additions and 165 deletions

View File

@@ -1,4 +1,5 @@
Imports DigitalData.Modules.Database
Imports System.Data.SqlClient
Imports DigitalData.Modules.Database
Imports DigitalData.Modules.EDMI.API
Imports DigitalData.Modules.EDMI.API.Constants
Imports DigitalData.Modules.EDMI.API.EDMIServiceReference
@@ -78,7 +79,7 @@ Public Class DatabaseWithFallback
End Function
Public Async Function GetDatatableECMAsync(pSQL As String) As Task(Of DataTable)
Return Await Task.Run(Function() GetDatatableECM(pSQL))
Return Await GetDatatableFromServiceAsync(pSQL, Constants.DatabaseType.ECM, 0)
End Function
Public Function GetDatatableIDB(pSQL As String) As DataTable
@@ -86,7 +87,7 @@ Public Class DatabaseWithFallback
End Function
Public Async Function GetDatatableIDBAsync(pSQL As String) As Task(Of DataTable)
Return Await Task.Run(Function() GetDatatableIDB(pSQL))
Return Await GetDatatableFromServiceAsync(pSQL, Constants.DatabaseType.IDB, 0)
End Function
Public Function GetDatatableWithConnection(pSQL As String, pConnectionId As Integer) As DataTable
@@ -94,7 +95,7 @@ Public Class DatabaseWithFallback
End Function
Public Async Function GetDatatableWithConnectionAsync(pSQL As String, pConnectionId As Integer) As Task(Of DataTable)
Return Await Task.Run(Function() GetDatatableWithConnection(pSQL, pConnectionId))
Return Await GetDatatableFromServiceAsync(pSQL, Constants.DatabaseType.None, pConnectionId)
End Function
#End Region
@@ -104,7 +105,7 @@ Public Class DatabaseWithFallback
End Function
Public Async Function GetScalarValueECMAsync(pSQL As String) As Task(Of Object)
Return Await Task.Run(Function() GetScalarValueECM(pSQL))
Return Await GetScalarValueFromServiceAsync(pSQL, Constants.DatabaseType.ECM, 0)
End Function
Public Function GetScalarValueIDB(pSQL As String) As Object
@@ -112,7 +113,7 @@ Public Class DatabaseWithFallback
End Function
Public Async Function GetScalarValueIDBAsync(pSQL As String) As Task(Of Object)
Return Await Task.Run(Function() GetScalarValueIDB(pSQL))
Return Await GetScalarValueFromServiceAsync(pSQL, Constants.DatabaseType.IDB, 0)
End Function
Public Function GetScalarValueWithConnection(pSQL As String, pConnectionId As Integer) As Object
@@ -120,7 +121,7 @@ Public Class DatabaseWithFallback
End Function
Public Async Function GetScalarValueWithConnectionAsync(pSQL As String, pConnectionId As Integer) As Task(Of Object)
Return Await Task.Run(Function() GetScalarValueWithConnection(pSQL, pConnectionId))
Return Await GetScalarValueFromServiceAsync(pSQL, Constants.DatabaseType.None, pConnectionId)
End Function
#End Region
@@ -130,7 +131,7 @@ Public Class DatabaseWithFallback
End Function
Public Async Function ExecuteNonQueryECMAsync(pSQL As String) As Task(Of Boolean)
Return Await Task.Run(Function() ExecuteNonQueryECM(pSQL))
Return Await ExecuteNonQueryFromServiceAsync(pSQL, Constants.DatabaseType.ECM, 0)
End Function
Public Function ExecuteNonQueryIDB(pSQL As String) As Boolean
@@ -138,7 +139,7 @@ Public Class DatabaseWithFallback
End Function
Public Async Function ExecuteNonQueryIDBAsync(pSQL As String) As Task(Of Boolean)
Return Await Task.Run(Function() ExecuteNonQueryIDB(pSQL))
Return Await ExecuteNonQueryFromServiceAsync(pSQL, Constants.DatabaseType.IDB, 0)
End Function
Public Function ExecuteNonQueryWithConnection(pSQL As String, pConnectionId As Integer) As Boolean
@@ -146,7 +147,7 @@ Public Class DatabaseWithFallback
End Function
Public Async Function ExecuteNonQueryWithConnectionAsync(pSQL As String, pConnectionId As Integer) As Task(Of Boolean)
Return Await Task.Run(Function() ExecuteNonQueryWithConnection(pSQL, pConnectionId))
Return Await ExecuteNonQueryFromServiceAsync(pSQL, Constants.DatabaseType.None, pConnectionId)
End Function
#End Region
@@ -187,33 +188,34 @@ Public Class DatabaseWithFallback
Try
Dim oResult As DataTable = Nothing
Dim oTableResult As TableResult = Nothing
_Logger.Debug("GetDatatable: Fetching datatable [{0}] , Filter Expression [{1}], Sort By Column [{2}], Force Fallback [{3}]",
pDataTableName, pFilterExpression, pSortByColumn, pForceFallback)
' If there is no client, we assume there is no service (configured)
If _Client Is Nothing Then
_Logger.Debug("Client is empty, falling back to direct database access.")
_Logger.Debug("GetDatatable: Client is empty, falling back to direct database access.")
Return GetDatatableFromDatabase(pFallbackSQL, pFallbackType, pConnectionId)
End If
' If ForceFallback flag is set, we go to database immediately
If pForceFallback Or _ClientConfig.ForceDirectDatabaseAccess Then
_Logger.Debug("ForceFallback is True, falling back to direct database access.")
_Logger.Debug("GetDatatable: ForceFallback is True, falling back to direct database access.")
Return GetDatatableFromDatabase(pFallbackSQL, pFallbackType, pConnectionId)
End If
' If the table is not cached, we try going through the service
If Not IsTableCached(pDataTableName) Then
_Logger.Debug("Datatable is not chached, fetching data from service.")
_Logger.Debug("GetDatatable: Datatable [{0}] is not chached, fetching data from service.", pDataTableName)
Return GetDatatableFromService(pFallbackSQL, pFallbackType, pConnectionId)
End If
' If there is a proper ConnectionId, we try going through the service
If pConnectionId > 0 Then
_Logger.Debug("ConnectionId is set, fetching data from service.")
_Logger.Debug("GetDatatable: ConnectionId is set, fetching data from service.")
Return GetDatatableFromService(pFallbackSQL, pFallbackType, pConnectionId)
End If
Try
_Logger.Debug("Datatable is chached, fetching data from cache.")
_Logger.Debug("GetDatatable: Datatable is chached, fetching data from cache.")
oTableResult = _Client.GetDatatableByName(pDataTableName, pFilterExpression, pSortByColumn)
Catch ex As Exception
_Logger.Error(ex)
@@ -221,9 +223,10 @@ Public Class DatabaseWithFallback
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)
_Logger.Warn("⚠ GetDatatable: Datatable [{0}] could not be fetched from AppServer Cache. Falling back to direct Database Access.", pDataTableName)
Return GetDatatableFromDatabase(pFallbackSQL, pFallbackType, pConnectionId)
Else
_Logger.Debug("✔ GetDatatable: Datatable [{0}] fetched from AppServer Cache.", pDataTableName)
Return oTableResult.Table
End If
@@ -276,11 +279,13 @@ Public Class DatabaseWithFallback
' If there is no client, we assume there is no service (configured)
If _Client Is Nothing Then
_Logger.Debug("ExecuteNonQuery - Client is empty, falling back to direct database access.")
Return ExecuteNonQueryFromDatabase(pSQL, pDatabaseType, pConnectionId)
End If
' If ForceFallback flag is set, we go to database immediately
If pForceFallback Or _ClientConfig.ForceDirectDatabaseAccess Then
_Logger.Debug("ExecuteNonQuery - ForceFallback is set, falling back to direct database access.")
Return ExecuteNonQueryFromDatabase(pSQL, pDatabaseType, pConnectionId)
End If
@@ -294,7 +299,9 @@ Public Class DatabaseWithFallback
End Function
Private Function IsTableCached(pName As String) As Boolean
_Logger.Debug("IsTableCached: Checking if table [{0}] is cached.", pName)
If _Client Is Nothing Then
_Logger.Warn("⚠ IsTableCached: Client is nothing, cannot check if table is cached.")
Return False
End If
@@ -303,42 +310,74 @@ Public Class DatabaseWithFallback
Private Function GetDatatableFromService(pSQLCommand As String, DatabaseType As Constants.DatabaseType, pConnectionId As Integer) As DataTable
Try
_Logger.Debug("GetDatatableFromService: Fetching data from service [{0}] with Connection Id [{1}] and Command [{2}]", DatabaseType.ToString, pConnectionId, pSQLCommand)
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)
If oResult Is Nothing OrElse oResult.OK = False Then
_Logger.Warn("⚠ GetDatatableFromService: Service returned invalid response [{0}]. Falling back to direct database access.", If(oResult Is Nothing, "null", oResult.ErrorMessage))
Return GetDatatableFromDatabase(pSQLCommand, DatabaseType, pConnectionId)
End If
Return oResult.Table
Catch ex As InvalidOperationException
_Logger.Warn("⚠ GetDatatableFromService skipped: [{0}] Falling back to direct database access.", ex.Message)
Return GetDatatableFromDatabase(pSQLCommand, DatabaseType, pConnectionId)
Catch ex As Exception
_Logger.Warn("GetDatatableFromService failed. Falling back to direct database access.")
_Logger.Warn("⚠ GetDatatableFromService failed. Falling back to direct database access.")
_Logger.Error(ex)
Return GetDatatableFromDatabase(pSQLCommand, DatabaseType, pConnectionId)
End Try
End Function
Private Async Function GetDatatableFromServiceAsync(pSQLCommand As String, DatabaseType As Constants.DatabaseType, pConnectionId As Integer) As Task(Of DataTable)
Dim oFallback As Boolean = False
_Logger.Debug("GetDatatableFromServiceAsync: GetDatatableFromServiceAsync called with SQL [{0}], DatabaseType [{1}], ConnectionId [{2}]", pSQLCommand, DatabaseType.ToString, pConnectionId)
If _Client Is Nothing OrElse _ClientConfig?.ForceDirectDatabaseAccess Then
Return Await Task.Run(Function() GetDatatableFromDatabase(pSQLCommand, DatabaseType, pConnectionId))
End If
Dim oResult As GetDatatableResponse = Nothing
Try
Select Case DatabaseType
Case Constants.DatabaseType.ECM
oResult = Await _Client.GetDatatableFromECMAsync(pSQLCommand)
Case Constants.DatabaseType.IDB
oResult = Await _Client.GetDatatableFromIDBAsync(pSQLCommand)
Case Else
oResult = Await _Client.GetDatatableFromConnectionAsync(pSQLCommand, pConnectionId)
End Select
If oResult Is Nothing OrElse oResult.OK = False Then
_Logger.Warn("⚠ GetDatatableFromServiceAsync failed. Falling back to direct database access.")
oFallback = True
End If
Catch ex As Exception
_Logger.Warn("⚠ GetDatatableFromServiceAsync failed. Falling back to direct database access.")
_Logger.Error(ex)
oFallback = True
End Try
If oFallback Then
Return Await Task.Run(Function() GetDatatableFromDatabase(pSQLCommand, DatabaseType, pConnectionId))
End If
Return oResult.Table
End Function
Private Function GetDatatableFromDatabase(pSQLCommand As String, pDatabaseType As Constants.DatabaseType, pConnectionId As Integer) As DataTable
Try
_Logger.Debug("Fetching data from database [{0}] with Connection Id [{0}]", pDatabaseType.ToString, pConnectionId)
_Logger.Debug("GetDatatableFromDatabase: Fetching data from database [{0}] with Connection Id [{1}] and Command [{2}]", pDatabaseType.ToString, pConnectionId, pSQLCommand)
Select Case pDatabaseType
Case Constants.DatabaseType.IDB
@@ -361,35 +400,68 @@ Public Class DatabaseWithFallback
End Try
End Function
Private Async Function GetScalarValueFromServiceAsync(pSQLCommand As String, DatabaseType As Constants.DatabaseType, pConnectionId As Integer) As Task(Of Object)
Dim oFallback As Boolean = False
_Logger.Debug("GetScalarValueFromServiceAsync called with SQL [{0}], DatabaseType [{1}], ConnectionId [{2}]", pSQLCommand, DatabaseType.ToString, pConnectionId)
If _Client Is Nothing OrElse _ClientConfig?.ForceDirectDatabaseAccess Then
Return Await Task.Run(Function() GetScalarValueFromDatabase(pSQLCommand, DatabaseType, pConnectionId))
End If
Dim oResult As GetScalarValueResponse = Nothing
Try
Select Case DatabaseType
Case Constants.DatabaseType.ECM
oResult = Await _Client.GetScalarValueFromECMAsync(pSQLCommand)
Case Constants.DatabaseType.IDB
oResult = Await _Client.GetScalarValueFromIDBAsync(pSQLCommand)
Case Else
oResult = Await _Client.GetScalarValueFromConnectionAsync(pSQLCommand, pConnectionId)
End Select
If oResult Is Nothing OrElse oResult.OK = False Then
_Logger.Warn("⚠ GetScalarValueFromServiceAsync failed. Falling back to direct database access.")
oFallback = True
End If
Catch ex As Exception
_Logger.Warn("⚠ GetScalarValueFromServiceAsync failed. Falling back to direct database access.")
_Logger.Error(ex)
oFallback = True
End Try
If oFallback Then
Return Await Task.Run(Function() GetScalarValueFromDatabase(pSQLCommand, DatabaseType, pConnectionId))
End If
Return oResult.Scalar
End Function
Private Function GetScalarValueFromService(pSQLCommand As String, DatabaseType As Constants.DatabaseType, pConnectionId As Integer) As Object
Try
Dim oResult As GetScalarValueResponse = Nothing
_Logger.Debug("GetScalarValueFromService [{0}] with Connection Id [{1}] and Command [{2}]", DatabaseType.ToString, pConnectionId, pSQLCommand)
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)
If oResult Is Nothing OrElse oResult.OK = False Then
_Logger.Warn("⚠ GetScalarValueFromService: Service returned invalid response [{0}]. Falling back to direct database access.", If(oResult Is Nothing, "null", oResult.ErrorMessage))
Return GetScalarValueFromDatabase(pSQLCommand, DatabaseType, pConnectionId)
End If
Return oResult.Scalar
Catch ex As InvalidOperationException
_Logger.Warn("⚠ GetScalarValueFromService skipped: [{0}] Falling back to direct database access.", ex.Message)
Return GetScalarValueFromDatabase(pSQLCommand, DatabaseType, pConnectionId)
Catch ex As Exception
_Logger.Warn("GetScalarValueFromService failed. Falling back to direct database access.")
_Logger.Warn("⚠ GetScalarValueFromService failed. Falling back to direct database access.")
_Logger.Error(ex)
Return GetScalarValueFromDatabase(pSQLCommand, DatabaseType, pConnectionId)
@@ -399,6 +471,7 @@ Public Class DatabaseWithFallback
Private Function GetScalarValueFromDatabase(pSQLCommand As String, DatabaseType As Constants.DatabaseType, pConnectionId As Integer) As Object
Try
_Logger.Debug("DBwFF GetScalarValueFromDatabase [{0}] with Connection Id [{1}] and Command [{2}]", DatabaseType.ToString, pConnectionId, pSQLCommand)
Select Case DatabaseType
Case Constants.DatabaseType.IDB
Return _DatabaseIDB.GetScalarValue(pSQLCommand)
@@ -419,35 +492,68 @@ Public Class DatabaseWithFallback
End Try
End Function
Private Async Function ExecuteNonQueryFromServiceAsync(pSQLCommand As String, DatabaseType As Constants.DatabaseType, pConnectionId As Integer) As Task(Of Boolean)
Dim oFallback As Boolean = False
_Logger.Debug("ExecuteNonQueryFromServiceAsync called with SQL [{0}], DatabaseType [{1}], ConnectionId [{2}]", pSQLCommand, DatabaseType.ToString, pConnectionId)
If _Client Is Nothing OrElse _ClientConfig?.ForceDirectDatabaseAccess Then
Return Await Task.Run(Function() ExecuteNonQueryFromDatabase(pSQLCommand, DatabaseType, pConnectionId))
End If
Dim oResult As ExecuteNonQueryResponse = Nothing
Try
Select Case DatabaseType
Case Constants.DatabaseType.ECM
oResult = Await _Client.ExecuteNonQueryFromECMAsync(pSQLCommand)
Case Constants.DatabaseType.IDB
oResult = Await _Client.ExecuteNonQueryFromIDBAsync(pSQLCommand)
Case Else
oResult = Await _Client.ExecuteNonQueryFromConnectionAsync(pSQLCommand, pConnectionId)
End Select
If oResult Is Nothing OrElse oResult.OK = False Then
_Logger.Warn("⚠ ExecuteNonQueryFromServiceAsync failed. Falling back to direct database access.")
oFallback = True
End If
Catch ex As Exception
_Logger.Warn("⚠ ExecuteNonQueryFromServiceAsync failed. Falling back to direct database access.")
_Logger.Error(ex)
oFallback = True
End Try
If oFallback Then
Return Await Task.Run(Function() ExecuteNonQueryFromDatabase(pSQLCommand, DatabaseType, pConnectionId))
End If
Return oResult.Result
End Function
Private Function ExecuteNonQueryFromService(pSQLCommand As String, DatabaseType As Constants.DatabaseType, pConnectionId As Integer) As Boolean
Try
Dim oResult As ExecuteNonQueryResponse = Nothing
_Logger.Debug("ExecuteNonQueryFromService [{0}] with Connection Id [{1}] and Command [{2}]", DatabaseType.ToString, pConnectionId, pSQLCommand)
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)
If oResult Is Nothing OrElse oResult.OK = False Then
_Logger.Warn("⚠ ExecuteNonQueryFromService: Service returned invalid response [{0}]. Falling back to direct database access.", If(oResult Is Nothing, "null", oResult.ErrorMessage))
Return ExecuteNonQueryFromDatabase(pSQLCommand, DatabaseType, pConnectionId)
End If
Return oResult.Result
Catch ex As InvalidOperationException
_Logger.Warn("⚠ ExecuteNonQueryFromService skipped: [{0}] Falling back to direct database access.", ex.Message)
Return ExecuteNonQueryFromDatabase(pSQLCommand, DatabaseType, pConnectionId)
Catch ex As Exception
_Logger.Warn("ExecuteNonQueryFromService failed. Falling back to direct database access.")
_Logger.Warn("⚠ ExecuteNonQueryFromService failed. Falling back to direct database access.")
_Logger.Error(ex)
Return ExecuteNonQueryFromDatabase(pSQLCommand, DatabaseType, pConnectionId)
@@ -456,6 +562,8 @@ Public Class DatabaseWithFallback
Private Function ExecuteNonQueryFromDatabase(pSQLCommand As String, DatabaseType As Constants.DatabaseType, pConnectionId As Integer) As Boolean
Try
_Logger.Debug("ExecuteNonQueryFromDatabase [{0}] with Connection Id [{1}] and Command [{2}]", DatabaseType.ToString, pConnectionId, pSQLCommand)
Select Case DatabaseType
Case Constants.DatabaseType.IDB
Return _DatabaseIDB.ExecuteNonQuery(pSQLCommand)