Verbesserungen: Thread-Sicherheit, Logging, WCF-Updates
Die Änderungen umfassen: - Einführung von `ReaderWriterLockSlim` für Thread-Sicherheit. - Optimierung von `IsOnline` mit atomaren Operationen. - Verbesserte Reconnect-Logik und Fehlerbehandlung. - Neue Methoden für Datenbankoperationen (`GetDatatableByName` etc.). - Gemeinsame `ChannelFactory` zur Ressourcenschonung. - Logging-Verbesserungen mit präziseren Meldungen und Symbolen. - Aktualisierte WCF-Bindings für höhere Verbindungszahlen und kürzere Timeouts. - Aktualisierte Service-Referenzen mit asynchronen Methoden. - Bereinigung von veralteten Methoden und Eigenschaften. - Assembly-Version auf `2.8.0.0` aktualisiert.
This commit is contained in:
@@ -22,8 +22,45 @@ Public Class Client
|
||||
' Runtime Variables
|
||||
Private _ServerAddress As ServerAddressStruct
|
||||
Private _ClientConfig As GlobalStateClientConfiguration
|
||||
|
||||
Private ReadOnly _CachedTablesLock As New ReaderWriterLockSlim()
|
||||
Private _CachedTables As New List(Of String)
|
||||
Private _IsOnline As Boolean
|
||||
|
||||
Public ReadOnly Property CachedTables As List(Of String)
|
||||
Get
|
||||
_CachedTablesLock.EnterReadLock()
|
||||
Try
|
||||
Return _CachedTables.ToList() ' Snapshot zurückgeben
|
||||
Finally
|
||||
_CachedTablesLock.ExitReadLock()
|
||||
End Try
|
||||
End Get
|
||||
End Property
|
||||
|
||||
' Hilfsmethode – intern für alle Schreibzugriffe verwenden
|
||||
Private Sub SetCachedTables(pTables As IEnumerable(Of String))
|
||||
_CachedTablesLock.EnterWriteLock()
|
||||
Try
|
||||
_CachedTables = pTables.ToList()
|
||||
Finally
|
||||
_CachedTablesLock.ExitWriteLock()
|
||||
End Try
|
||||
End Sub
|
||||
' Ersetzt: Private _IsOnline As Boolean
|
||||
Private _IsOnline As Integer = 0 ' 0 = offline, 1 = online (Interlocked-kompatibel)
|
||||
Public ReadOnly Property IsOnline As Boolean
|
||||
Get
|
||||
Return Interlocked.CompareExchange(_IsOnline, 0, 0) = 1
|
||||
End Get
|
||||
End Property
|
||||
' Setter-Hilfsmethoden für alle Schreibstellen:
|
||||
Private Sub SetOnline()
|
||||
Interlocked.Exchange(_IsOnline, 1)
|
||||
End Sub
|
||||
|
||||
Private Sub SetOffline()
|
||||
Interlocked.Exchange(_IsOnline, 0)
|
||||
End Sub
|
||||
' Verhindert parallele Reconnect-Versuche (0 = frei, 1 = läuft)
|
||||
Private _IsReconnecting As Integer = 0
|
||||
Private _Channel As IEDMIServiceChannel
|
||||
@@ -56,14 +93,14 @@ Public Class Client
|
||||
Protected Overridable Sub Dispose(disposing As Boolean)
|
||||
If _Disposed Then Return
|
||||
If disposing Then
|
||||
_IsOnline = False ' ← Reconnect-Guard deaktivieren
|
||||
SetOffline()
|
||||
UpdateTimer.Stop()
|
||||
UpdateTimer.Dispose()
|
||||
|
||||
' Auf laufenden Reconnect warten
|
||||
Dim oTimeout = DateTime.Now.AddSeconds(5)
|
||||
While _IsReconnecting = 1 AndAlso DateTime.Now < oTimeout
|
||||
Task.Delay(100).Wait()
|
||||
Thread.Sleep(100)
|
||||
End While
|
||||
|
||||
If _Channel IsNot Nothing Then
|
||||
@@ -71,6 +108,8 @@ Public Class Client
|
||||
_Channel = Nothing
|
||||
End If
|
||||
|
||||
_CachedTablesLock.Dispose() ' ← NEU: Lock freigeben
|
||||
|
||||
Dim oRemoved As ClientStatus = Nothing
|
||||
_ActiveClients.TryRemove(_ClientId, oRemoved)
|
||||
Logger.Debug("Client [{0}] unregistered. Total active clients: [{1}]", _ClientName, _ActiveClients.Count)
|
||||
@@ -79,11 +118,6 @@ Public Class Client
|
||||
End Sub
|
||||
|
||||
' Public Variables
|
||||
Public ReadOnly Property CachedTables
|
||||
Get
|
||||
Return _CachedTables
|
||||
End Get
|
||||
End Property
|
||||
|
||||
Public ReadOnly Property ClientConfig As GlobalStateClientConfiguration
|
||||
Get
|
||||
@@ -94,13 +128,6 @@ Public Class Client
|
||||
Return _ClientConfig
|
||||
End Get
|
||||
End Property
|
||||
|
||||
Public ReadOnly Property IsOnline As Boolean
|
||||
Get
|
||||
Return _IsOnline
|
||||
End Get
|
||||
End Property
|
||||
|
||||
Public ReadOnly Property ServerAddress As String
|
||||
Get
|
||||
Return $"{_ServerAddress.Host}:{_ServerAddress.Port}"
|
||||
@@ -197,14 +224,14 @@ Public Class Client
|
||||
' CachedTables sofort befüllen – nicht auf den ersten Timer-Tick warten
|
||||
Try
|
||||
Dim oTables As String() = _Channel.GetCachedTables()
|
||||
_CachedTables = oTables.Select(Function(t) t.ToUpper).ToList()
|
||||
Logger.Debug("✔ CachedTables loaded on connect: [{0}]", _CachedTables.Count)
|
||||
SetCachedTables(oTables.Select(Function(t) t.ToUpper()))
|
||||
Logger.Debug("✔ CachedTables loaded on connect: [{0}]", CachedTables.Count)
|
||||
Catch ex As Exception
|
||||
Logger.Warn("⚠ CachedTables could not be loaded on connect: {0}", ex.Message)
|
||||
_CachedTables = New List(Of String)
|
||||
SetCachedTables(Enumerable.Empty(Of String)())
|
||||
End Try
|
||||
|
||||
_IsOnline = True ' ← vor GetStatus()
|
||||
SetOnline()
|
||||
UpdateTimer.Start()
|
||||
|
||||
' In Registry eintragen – jetzt mit korrektem IsOnline=True
|
||||
@@ -213,7 +240,7 @@ Public Class Client
|
||||
|
||||
Return True
|
||||
Catch ex As Exception
|
||||
_IsOnline = False
|
||||
SetOffline()
|
||||
Logger.Error(ex)
|
||||
Return False
|
||||
End Try
|
||||
@@ -243,7 +270,7 @@ Public Class Client
|
||||
_Channel = ChannelManager.GetChannel()
|
||||
_Channel.Open()
|
||||
|
||||
_IsOnline = True
|
||||
SetOnline()
|
||||
|
||||
If _ActiveClients.ContainsKey(_ClientId) Then
|
||||
_ActiveClients(_ClientId) = GetStatus()
|
||||
@@ -253,7 +280,7 @@ Public Class Client
|
||||
Return
|
||||
Catch ex As Exception
|
||||
Logger.Warn("⚠ Reconnect failed after [{0}]s: {1}", oDelay, ex.Message)
|
||||
_IsOnline = False
|
||||
SetOffline()
|
||||
End Try
|
||||
Next
|
||||
|
||||
@@ -266,53 +293,51 @@ Public Class Client
|
||||
|
||||
Private Async Sub UpdateTimer_Elapsed(sender As Object, e As Timers.ElapsedEventArgs) Handles UpdateTimer.Elapsed
|
||||
Try
|
||||
If _Channel Is Nothing OrElse _Channel.State <> CommunicationState.Opened Then
|
||||
Logger.Warn("⚠ UpdateTimer skipped: Channel not in Opened state [{0}]", If(_Channel IsNot Nothing, _Channel.State.ToString(), "null"))
|
||||
_IsOnline = False
|
||||
' ← Snapshot VOR dem Await – schützt gegen _Channel = Nothing nach Resume
|
||||
Dim oChannel = _Channel
|
||||
If oChannel Is Nothing OrElse oChannel.State <> CommunicationState.Opened Then
|
||||
Logger.Warn("⚠ UpdateTimer skipped: Channel not in Opened state [{0}]",
|
||||
If(oChannel IsNot Nothing, oChannel.State.ToString(), "null"))
|
||||
SetOffline()
|
||||
Reconnect(sender, e)
|
||||
Return
|
||||
End If
|
||||
|
||||
If Not _IsOnline Then
|
||||
If Not IsOnline Then
|
||||
Logger.Debug("UpdateTimer skipped: Client is offline.")
|
||||
Return
|
||||
End If
|
||||
|
||||
Dim oTables As String() = Await _Channel.GetCachedTablesAsync()
|
||||
' ◄ NullReferenceException-Schutz: Service kann Nothing zurückgeben
|
||||
Dim oTables As String() = Await oChannel.GetCachedTablesAsync() ' ← oChannel statt _Channel
|
||||
|
||||
If oTables Is Nothing Then
|
||||
Logger.Warn("⚠ UpdateTimer: GetCachedTablesAsync returned Nothing.")
|
||||
_CachedTables = New List(Of String)
|
||||
SetCachedTables(Enumerable.Empty(Of String)())
|
||||
Return
|
||||
End If
|
||||
_CachedTables = oTables.
|
||||
Select(Function(table) table.ToUpper).
|
||||
ToList()
|
||||
|
||||
SetCachedTables(oTables.Select(Function(table) table.ToUpper()))
|
||||
|
||||
' ← oChannel.State statt _Channel.State
|
||||
Logger.Info("✔ Channel state: [{0}] | Active connections: [{1}] | CachedTables: [{2}]",
|
||||
_Channel.State,
|
||||
Channel.ActiveConnections,
|
||||
_CachedTables.Count)
|
||||
oChannel.State, Channel.ActiveConnections, CachedTables.Count)
|
||||
|
||||
If _ActiveClients.ContainsKey(_ClientId) Then
|
||||
_ActiveClients(_ClientId) = GetStatus()
|
||||
End If
|
||||
|
||||
Catch ex As TimeoutException
|
||||
' Stale TCP connection (z.B. Firewall/Citrix Idle-Timeout)
|
||||
Logger.Warn("⚠ UpdateTimer: TCP connection lost (idle timeout / firewall). Triggering reconnect...")
|
||||
_IsOnline = False
|
||||
Logger.Warn("⚠ UpdateTimer: TCP connection lost. Triggering reconnect...")
|
||||
SetOffline()
|
||||
Reconnect()
|
||||
|
||||
Catch ex As CommunicationException
|
||||
Logger.Warn("⚠ UpdateTimer: Channel faulted. Triggering reconnect...")
|
||||
_IsOnline = False
|
||||
SetOffline()
|
||||
Reconnect()
|
||||
|
||||
Catch ex As Exception
|
||||
Logger.Warn("⚠ Update of CachedTable was not successful!")
|
||||
Logger.Error(ex)
|
||||
_CachedTables = New List(Of String)
|
||||
SetCachedTables(Enumerable.Empty(Of String)())
|
||||
End Try
|
||||
End Sub
|
||||
|
||||
@@ -626,6 +651,59 @@ Public Class Client
|
||||
End Function
|
||||
|
||||
#Region "GetDatatable"
|
||||
Public Function GetDatatableByName(DatatableName As String, Optional FilterExpression As String = "", Optional SortByColumn As String = "") As TableResult
|
||||
EnsureAvailable()
|
||||
Try
|
||||
Return _Channel.ReturnDatatableFromCache(DatatableName, FilterExpression, SortByColumn)
|
||||
Catch ex As TimeoutException
|
||||
HandleCommunicationError(ex, NameOf(GetDatatableByName))
|
||||
Catch ex As EndpointNotFoundException
|
||||
HandleCommunicationError(ex, NameOf(GetDatatableByName))
|
||||
Catch ex As CommunicationException
|
||||
HandleCommunicationError(ex, NameOf(GetDatatableByName))
|
||||
Catch ex As Exception
|
||||
Logger.Error(ex)
|
||||
Throw
|
||||
End Try
|
||||
End Function
|
||||
|
||||
Public Function GetScalarValueFromIDB(pSQL As String) As GetScalarValueResponse
|
||||
EnsureAvailable()
|
||||
Try
|
||||
Return _Channel.ReturnScalarValue(New GetScalarValueRequest() With {
|
||||
.SqlCommand = pSQL,
|
||||
.NamedDatabase = DatabaseName.IDB
|
||||
})
|
||||
Catch ex As TimeoutException
|
||||
HandleCommunicationError(ex, NameOf(GetScalarValueFromIDB))
|
||||
Catch ex As EndpointNotFoundException
|
||||
HandleCommunicationError(ex, NameOf(GetScalarValueFromIDB))
|
||||
Catch ex As CommunicationException
|
||||
HandleCommunicationError(ex, NameOf(GetScalarValueFromIDB))
|
||||
Catch ex As Exception
|
||||
Logger.Error(ex)
|
||||
Throw
|
||||
End Try
|
||||
End Function
|
||||
|
||||
Public Function ExecuteNonQueryFromIDB(pSQL As String) As ExecuteNonQueryResponse
|
||||
EnsureAvailable()
|
||||
Try
|
||||
Return _Channel.ExecuteNonQuery(New ExecuteNonQueryRequest() With {
|
||||
.SqlCommand = pSQL,
|
||||
.NamedDatabase = DatabaseName.IDB
|
||||
})
|
||||
Catch ex As TimeoutException
|
||||
HandleCommunicationError(ex, NameOf(ExecuteNonQueryFromIDB))
|
||||
Catch ex As EndpointNotFoundException
|
||||
HandleCommunicationError(ex, NameOf(ExecuteNonQueryFromIDB))
|
||||
Catch ex As CommunicationException
|
||||
HandleCommunicationError(ex, NameOf(ExecuteNonQueryFromIDB))
|
||||
Catch ex As Exception
|
||||
Logger.Error(ex)
|
||||
Throw
|
||||
End Try
|
||||
End Function
|
||||
Public Function GetDatatableFromIDB(pSQL As String) As GetDatatableResponse
|
||||
EnsureAvailable()
|
||||
Try
|
||||
@@ -633,6 +711,8 @@ Public Class Client
|
||||
.SqlCommand = pSQL,
|
||||
.NamedDatabase = DatabaseName.IDB
|
||||
})
|
||||
Catch ex As TimeoutException ' ← NEU
|
||||
HandleCommunicationError(ex, NameOf(GetDatatableFromIDB))
|
||||
Catch ex As EndpointNotFoundException
|
||||
HandleCommunicationError(ex, NameOf(GetDatatableFromIDB))
|
||||
Catch ex As CommunicationException
|
||||
@@ -643,6 +723,7 @@ Public Class Client
|
||||
End Try
|
||||
End Function
|
||||
|
||||
' ── GetDatatable ──────────────────────────────────────────
|
||||
Public Function GetDatatableFromECM(pSQL As String) As GetDatatableResponse
|
||||
EnsureAvailable()
|
||||
Try
|
||||
@@ -650,6 +731,8 @@ Public Class Client
|
||||
.SqlCommand = pSQL,
|
||||
.NamedDatabase = DatabaseName.ECM
|
||||
})
|
||||
Catch ex As TimeoutException
|
||||
HandleCommunicationError(ex, NameOf(GetDatatableFromECM))
|
||||
Catch ex As EndpointNotFoundException
|
||||
HandleCommunicationError(ex, NameOf(GetDatatableFromECM))
|
||||
Catch ex As CommunicationException
|
||||
@@ -668,6 +751,8 @@ Public Class Client
|
||||
.NamedDatabase = DatabaseName.None,
|
||||
.ConnectionId = pConnectionId
|
||||
})
|
||||
Catch ex As TimeoutException
|
||||
HandleCommunicationError(ex, NameOf(GetDatatableFromConnection))
|
||||
Catch ex As EndpointNotFoundException
|
||||
HandleCommunicationError(ex, NameOf(GetDatatableFromConnection))
|
||||
Catch ex As CommunicationException
|
||||
@@ -685,6 +770,8 @@ Public Class Client
|
||||
.SqlCommand = pSQL,
|
||||
.NamedDatabase = DatabaseName.IDB
|
||||
})
|
||||
Catch ex As TimeoutException
|
||||
HandleCommunicationError(ex, NameOf(GetDatatableFromIDBAsync))
|
||||
Catch ex As EndpointNotFoundException
|
||||
HandleCommunicationError(ex, NameOf(GetDatatableFromIDBAsync))
|
||||
Catch ex As CommunicationException
|
||||
@@ -703,6 +790,8 @@ Public Class Client
|
||||
.SqlCommand = pSQL,
|
||||
.NamedDatabase = DatabaseName.ECM
|
||||
})
|
||||
Catch ex As TimeoutException
|
||||
HandleCommunicationError(ex, NameOf(GetDatatableFromECMAsync))
|
||||
Catch ex As EndpointNotFoundException
|
||||
HandleCommunicationError(ex, NameOf(GetDatatableFromECMAsync))
|
||||
Catch ex As CommunicationException
|
||||
@@ -721,6 +810,8 @@ Public Class Client
|
||||
.NamedDatabase = DatabaseName.None,
|
||||
.ConnectionId = pConnectionId
|
||||
})
|
||||
Catch ex As TimeoutException
|
||||
HandleCommunicationError(ex, NameOf(GetDatatableFromConnectionAsync))
|
||||
Catch ex As EndpointNotFoundException
|
||||
HandleCommunicationError(ex, NameOf(GetDatatableFromConnectionAsync))
|
||||
Catch ex As CommunicationException
|
||||
@@ -730,26 +821,8 @@ Public Class Client
|
||||
Throw
|
||||
End Try
|
||||
End Function
|
||||
#End Region
|
||||
|
||||
#Region "GetScalarValue"
|
||||
Public Function GetScalarValueFromIDB(pSQL As String) As GetScalarValueResponse
|
||||
EnsureAvailable()
|
||||
Try
|
||||
Return _Channel.ReturnScalarValue(New GetScalarValueRequest() With {
|
||||
.SqlCommand = pSQL,
|
||||
.NamedDatabase = DatabaseName.IDB
|
||||
})
|
||||
Catch ex As EndpointNotFoundException
|
||||
HandleCommunicationError(ex, NameOf(GetScalarValueFromIDB))
|
||||
Catch ex As CommunicationException
|
||||
HandleCommunicationError(ex, NameOf(GetScalarValueFromIDB))
|
||||
Catch ex As Exception
|
||||
Logger.Error(ex)
|
||||
Throw
|
||||
End Try
|
||||
End Function
|
||||
|
||||
' ── GetScalarValue ─────────────────────────────────────────
|
||||
Public Function GetScalarValueFromECM(pSQL As String) As GetScalarValueResponse
|
||||
EnsureAvailable()
|
||||
Try
|
||||
@@ -757,6 +830,8 @@ Public Class Client
|
||||
.SqlCommand = pSQL,
|
||||
.NamedDatabase = DatabaseName.ECM
|
||||
})
|
||||
Catch ex As TimeoutException
|
||||
HandleCommunicationError(ex, NameOf(GetScalarValueFromECM))
|
||||
Catch ex As EndpointNotFoundException
|
||||
HandleCommunicationError(ex, NameOf(GetScalarValueFromECM))
|
||||
Catch ex As CommunicationException
|
||||
@@ -775,6 +850,8 @@ Public Class Client
|
||||
.NamedDatabase = DatabaseName.None,
|
||||
.ConnectionId = pConnectionId
|
||||
})
|
||||
Catch ex As TimeoutException
|
||||
HandleCommunicationError(ex, NameOf(GetScalarValueFromConnection))
|
||||
Catch ex As EndpointNotFoundException
|
||||
HandleCommunicationError(ex, NameOf(GetScalarValueFromConnection))
|
||||
Catch ex As CommunicationException
|
||||
@@ -792,6 +869,8 @@ Public Class Client
|
||||
.SqlCommand = pSQL,
|
||||
.NamedDatabase = DatabaseName.IDB
|
||||
})
|
||||
Catch ex As TimeoutException
|
||||
HandleCommunicationError(ex, NameOf(GetScalarValueFromIDBAsync))
|
||||
Catch ex As EndpointNotFoundException
|
||||
HandleCommunicationError(ex, NameOf(GetScalarValueFromIDBAsync))
|
||||
Catch ex As CommunicationException
|
||||
@@ -809,6 +888,8 @@ Public Class Client
|
||||
.SqlCommand = pSQL,
|
||||
.NamedDatabase = DatabaseName.ECM
|
||||
})
|
||||
Catch ex As TimeoutException
|
||||
HandleCommunicationError(ex, NameOf(GetScalarValueFromECMAsync))
|
||||
Catch ex As EndpointNotFoundException
|
||||
HandleCommunicationError(ex, NameOf(GetScalarValueFromECMAsync))
|
||||
Catch ex As CommunicationException
|
||||
@@ -827,6 +908,8 @@ Public Class Client
|
||||
.NamedDatabase = DatabaseName.None,
|
||||
.ConnectionId = pConnectionId
|
||||
})
|
||||
Catch ex As TimeoutException
|
||||
HandleCommunicationError(ex, NameOf(GetScalarValueFromConnectionAsync))
|
||||
Catch ex As EndpointNotFoundException
|
||||
HandleCommunicationError(ex, NameOf(GetScalarValueFromConnectionAsync))
|
||||
Catch ex As CommunicationException
|
||||
@@ -836,26 +919,8 @@ Public Class Client
|
||||
Throw
|
||||
End Try
|
||||
End Function
|
||||
#End Region
|
||||
|
||||
#Region "ExecuteNonQuery"
|
||||
Public Function ExecuteNonQueryFromIDB(pSQL As String) As ExecuteNonQueryResponse
|
||||
EnsureAvailable()
|
||||
Try
|
||||
Return _Channel.ExecuteNonQuery(New ExecuteNonQueryRequest() With {
|
||||
.SqlCommand = pSQL,
|
||||
.NamedDatabase = DatabaseName.IDB
|
||||
})
|
||||
Catch ex As EndpointNotFoundException
|
||||
HandleCommunicationError(ex, NameOf(ExecuteNonQueryFromIDB))
|
||||
Catch ex As CommunicationException
|
||||
HandleCommunicationError(ex, NameOf(ExecuteNonQueryFromIDB))
|
||||
Catch ex As Exception
|
||||
Logger.Error(ex)
|
||||
Throw
|
||||
End Try
|
||||
End Function
|
||||
|
||||
' ── ExecuteNonQuery ────────────────────────────────────────
|
||||
Public Function ExecuteNonQueryFromECM(pSQL As String) As ExecuteNonQueryResponse
|
||||
EnsureAvailable()
|
||||
Try
|
||||
@@ -863,6 +928,8 @@ Public Class Client
|
||||
.SqlCommand = pSQL,
|
||||
.NamedDatabase = DatabaseName.ECM
|
||||
})
|
||||
Catch ex As TimeoutException
|
||||
HandleCommunicationError(ex, NameOf(ExecuteNonQueryFromECM))
|
||||
Catch ex As EndpointNotFoundException
|
||||
HandleCommunicationError(ex, NameOf(ExecuteNonQueryFromECM))
|
||||
Catch ex As CommunicationException
|
||||
@@ -881,6 +948,8 @@ Public Class Client
|
||||
.NamedDatabase = DatabaseName.None,
|
||||
.ConnectionId = pConnectionId
|
||||
})
|
||||
Catch ex As TimeoutException
|
||||
HandleCommunicationError(ex, NameOf(ExecuteNonQueryFromConnection))
|
||||
Catch ex As EndpointNotFoundException
|
||||
HandleCommunicationError(ex, NameOf(ExecuteNonQueryFromConnection))
|
||||
Catch ex As CommunicationException
|
||||
@@ -898,6 +967,8 @@ Public Class Client
|
||||
.SqlCommand = pSQL,
|
||||
.NamedDatabase = DatabaseName.IDB
|
||||
})
|
||||
Catch ex As TimeoutException
|
||||
HandleCommunicationError(ex, NameOf(ExecuteNonQueryFromIDBAsync))
|
||||
Catch ex As EndpointNotFoundException
|
||||
HandleCommunicationError(ex, NameOf(ExecuteNonQueryFromIDBAsync))
|
||||
Catch ex As CommunicationException
|
||||
@@ -915,6 +986,8 @@ Public Class Client
|
||||
.SqlCommand = pSQL,
|
||||
.NamedDatabase = DatabaseName.ECM
|
||||
})
|
||||
Catch ex As TimeoutException
|
||||
HandleCommunicationError(ex, NameOf(ExecuteNonQueryFromECMAsync))
|
||||
Catch ex As EndpointNotFoundException
|
||||
HandleCommunicationError(ex, NameOf(ExecuteNonQueryFromECMAsync))
|
||||
Catch ex As CommunicationException
|
||||
@@ -933,6 +1006,8 @@ Public Class Client
|
||||
.NamedDatabase = DatabaseName.None,
|
||||
.ConnectionId = pConnectionId
|
||||
})
|
||||
Catch ex As TimeoutException
|
||||
HandleCommunicationError(ex, NameOf(ExecuteNonQueryFromConnectionAsync))
|
||||
Catch ex As EndpointNotFoundException
|
||||
HandleCommunicationError(ex, NameOf(ExecuteNonQueryFromConnectionAsync))
|
||||
Catch ex As CommunicationException
|
||||
@@ -942,45 +1017,26 @@ Public Class Client
|
||||
Throw
|
||||
End Try
|
||||
End Function
|
||||
|
||||
' ── Sonstige ───────────────────────────────────────────────
|
||||
Public Async Function GetDatatableByNameAsync(DatatableName As String, Optional FilterExpression As String = "", Optional SortByColumn As String = "") As Task(Of TableResult)
|
||||
EnsureAvailable()
|
||||
Try
|
||||
Return Await _Channel.ReturnDatatableFromCacheAsync(DatatableName, FilterExpression, SortByColumn)
|
||||
Catch ex As TimeoutException
|
||||
HandleCommunicationError(ex, NameOf(GetDatatableByNameAsync))
|
||||
Catch ex As EndpointNotFoundException
|
||||
HandleCommunicationError(ex, NameOf(GetDatatableByNameAsync))
|
||||
Catch ex As CommunicationException
|
||||
HandleCommunicationError(ex, NameOf(GetDatatableByNameAsync))
|
||||
Catch ex As Exception
|
||||
Logger.Error(ex)
|
||||
Throw
|
||||
End Try
|
||||
End Function
|
||||
#End Region
|
||||
|
||||
|
||||
Public Function GetDatatableByName(DatatableName As String, Optional FilterExpression As String = "", Optional SortByColumn As String = "") As TableResult
|
||||
EnsureAvailable()
|
||||
Try
|
||||
Return _Channel.ReturnDatatableFromCache(DatatableName, FilterExpression, SortByColumn)
|
||||
Catch ex As EndpointNotFoundException
|
||||
HandleCommunicationError(ex, NameOf(GetDatatableByName))
|
||||
Catch ex As CommunicationException
|
||||
HandleCommunicationError(ex, NameOf(GetDatatableByName))
|
||||
Catch ex As Exception
|
||||
Logger.Error(ex)
|
||||
Throw
|
||||
End Try
|
||||
End Function
|
||||
|
||||
Public Async Function GetDatatableByNameAsync(DatatableName As String, Optional FilterExpression As String = "", Optional SortByColumn As String = "") As Task(Of TableResult)
|
||||
EnsureAvailable()
|
||||
Try
|
||||
Return Await _Channel.ReturnDatatableFromCacheAsync(DatatableName, FilterExpression, SortByColumn)
|
||||
Catch ex As EndpointNotFoundException
|
||||
HandleCommunicationError(ex, NameOf(GetDatatableByNameAsync))
|
||||
Catch ex As CommunicationException
|
||||
HandleCommunicationError(ex, NameOf(GetDatatableByNameAsync))
|
||||
Catch ex As Exception
|
||||
Logger.Error(ex)
|
||||
Throw
|
||||
End Try
|
||||
End Function
|
||||
|
||||
|
||||
|
||||
''' <summary>
|
||||
''' Return infos about a file object
|
||||
''' </summary>
|
||||
''' <param name="UserId"></param>
|
||||
''' <param name="ObjectId"></param>
|
||||
''' <returns></returns>
|
||||
Public Function GetDocumentInfo(UserId As Long, ObjectId As Long) As DocumentInfo
|
||||
Try
|
||||
Dim oResponse As DocumentInfoResponse = _Channel.GetFileInfoByObjectId(New DocumentInfoRequest With {
|
||||
@@ -1056,27 +1112,25 @@ Public Class Client
|
||||
Throw New InvalidOperationException("Channel is not initialized. Call Connect() first.")
|
||||
End If
|
||||
|
||||
If Not _IsOnline Then
|
||||
' Kurze Grace-Period abwarten, damit Reconnect() seinen _IsReconnecting-Flag setzen kann
|
||||
Dim oGraceLimit = DateTime.Now.AddMilliseconds(600)
|
||||
While DateTime.Now < oGraceLimit AndAlso _IsReconnecting = 0 AndAlso Not _IsOnline
|
||||
Task.Delay(100).Wait()
|
||||
If Not IsOnline Then
|
||||
' Wenn Reconnect aktiv läuft: kurze Grace-Period (max 1s) statt 10s
|
||||
If _IsReconnecting = 1 Then
|
||||
Dim oGraceLimit = DateTime.Now.AddMilliseconds(1000)
|
||||
While DateTime.Now < oGraceLimit AndAlso Not IsOnline
|
||||
Thread.Sleep(100)
|
||||
End While
|
||||
End If
|
||||
|
||||
' Jetzt auf aktiven Reconnect warten
|
||||
Dim oWaitLimit = DateTime.Now.AddSeconds(10)
|
||||
While Not _IsOnline AndAlso DateTime.Now < oWaitLimit AndAlso _IsReconnecting = 1
|
||||
Task.Delay(500).Wait()
|
||||
Logger.Debug("EnsureAvailable: Waiting for reconnect... Channel=[{0}]", _Channel.State.ToString())
|
||||
End While
|
||||
|
||||
If Not _IsOnline Then
|
||||
Throw New InvalidOperationException("Client is offline.")
|
||||
' Danach sofort werfen – WCF-Timeout nicht weiter aufbrauchen
|
||||
If Not IsOnline Then
|
||||
Throw New InvalidOperationException(
|
||||
$"Client is offline. Reconnect in progress=[{_IsReconnecting = 1}].")
|
||||
End If
|
||||
End If
|
||||
|
||||
If _Channel.State <> CommunicationState.Opened Then
|
||||
Throw New InvalidOperationException($"Channel is not available. State=[{_Channel.State}]")
|
||||
Throw New InvalidOperationException(
|
||||
$"Channel is not available. State=[{_Channel.State}]")
|
||||
End If
|
||||
End Sub
|
||||
''' <summary>
|
||||
@@ -1084,6 +1138,8 @@ Public Class Client
|
||||
''' </summary>
|
||||
Private Sub HandleCommunicationError(ex As Exception, callerName As String)
|
||||
Select Case True
|
||||
Case TypeOf ex Is TimeoutException
|
||||
Logger.Warn("⚠ TimeoutException in [{0}]. Channel may be stale. Triggering reconnect...", callerName)
|
||||
Case TypeOf ex Is EndpointNotFoundException
|
||||
Logger.Warn("⚠ Service not reachable (EndpointNotFoundException in [{0}]). Triggering reconnect...", callerName)
|
||||
Case TypeOf ex Is CommunicationException
|
||||
@@ -1093,7 +1149,7 @@ Public Class Client
|
||||
Logger.Error(ex)
|
||||
End Select
|
||||
|
||||
_IsOnline = False
|
||||
SetOffline()
|
||||
Reconnect()
|
||||
Throw ex
|
||||
End Sub
|
||||
@@ -1190,15 +1246,16 @@ Public Class Client
|
||||
''' Returns a detailed status snapshot of the current client state
|
||||
''' </summary>
|
||||
Public Function GetStatus() As ClientStatus
|
||||
Dim oTables = CachedTables ' ← Property nutzen: enthält Read-Lock + Snapshot
|
||||
Return New ClientStatus() With {
|
||||
.IsOnline = _IsOnline,
|
||||
.ClientId = _ClientId, ' ← Neu
|
||||
.IsOnline = IsOnline, ' ← Property statt Feld
|
||||
.ClientId = _ClientId,
|
||||
.ClientName = _ClientName,
|
||||
.ServerAddress = Me.ServerAddress,
|
||||
.ChannelState = If(_Channel IsNot Nothing, _Channel.State.ToString(), "null"),
|
||||
.ActiveConnections = Channel.ActiveConnections,
|
||||
.CachedTableCount = _CachedTables.Count,
|
||||
.CachedTables = _CachedTables.ToList(),
|
||||
.CachedTableCount = oTables.Count, ' ← aus Snapshot
|
||||
.CachedTables = oTables, ' ← aus Snapshot
|
||||
.HasClientConfig = _ClientConfig IsNot Nothing,
|
||||
.IsDisposed = _Disposed,
|
||||
.TimerEnabled = UpdateTimer.Enabled,
|
||||
|
||||
@@ -8,10 +8,14 @@ Imports DigitalData.Modules.Logging
|
||||
Public Class Channel
|
||||
Inherits BaseClass
|
||||
|
||||
Private Shared ReadOnly _FactoryLock As New Object()
|
||||
Private Shared _SharedFactory As ChannelFactory(Of IEDMIServiceChannel) = Nothing
|
||||
Private Shared _SharedFactoryEndpoint As String = String.Empty
|
||||
|
||||
Private ReadOnly ChannelFactory As ChannelFactory(Of IEDMIServiceChannel)
|
||||
|
||||
Public Event Reconnect As EventHandler
|
||||
' Verbindungszähler
|
||||
|
||||
Private Shared _ActiveConnections As Integer = 0
|
||||
Public Shared ReadOnly Property ActiveConnections As Integer
|
||||
Get
|
||||
@@ -20,16 +24,18 @@ Public Class Channel
|
||||
End Property
|
||||
|
||||
Public Shared Function GetBinding(Optional AuthenticationMode As TcpClientCredentialType = TcpClientCredentialType.Windows) As NetTcpBinding
|
||||
Dim oMaxConnections = Math.Max(500, Environment.ProcessorCount * 50)
|
||||
|
||||
Return New NetTcpBinding() With {
|
||||
.MaxReceivedMessageSize = Constants.ChannelSettings.MAX_RECEIVED_MESSAGE_SIZE,
|
||||
.MaxBufferSize = Constants.ChannelSettings.MAX_BUFFER_SIZE,
|
||||
.MaxBufferPoolSize = Constants.ChannelSettings.MAX_BUFFER_POOL_SIZE,
|
||||
.ListenBacklog = Constants.ChannelSettings.MAX_CONNECTIONS,
|
||||
.MaxConnections = Constants.ChannelSettings.MAX_CONNECTIONS,
|
||||
.ListenBacklog = oMaxConnections,
|
||||
.MaxConnections = oMaxConnections,
|
||||
.TransferMode = TransferMode.Streamed,
|
||||
.SendTimeout = TimeSpan.FromSeconds(30), ' ← War: 5min → Schnellere Fehlererkennung
|
||||
.ReceiveTimeout = TimeSpan.FromMinutes(10), ' Keepalive < Firewall-Timeout (3600s) ✓
|
||||
.OpenTimeout = TimeSpan.FromSeconds(30), ' ← Neu: Verbindungsaufbau begrenzen
|
||||
.SendTimeout = TimeSpan.FromSeconds(60),
|
||||
.ReceiveTimeout = TimeSpan.FromMinutes(10),
|
||||
.OpenTimeout = TimeSpan.FromSeconds(60),
|
||||
.CloseTimeout = TimeSpan.FromSeconds(30),
|
||||
.Security = New NetTcpSecurity() With {
|
||||
.Mode = SecurityMode.Transport,
|
||||
@@ -46,14 +52,63 @@ Public Class Channel
|
||||
|
||||
Public Sub New(pLogConfig As LogConfig, pServerAddress As ServerAddressStruct)
|
||||
MyBase.New(pLogConfig)
|
||||
ChannelFactory = GetChannelFactory(pServerAddress)
|
||||
ChannelFactory = GetOrCreateSharedFactory(pLogConfig, pServerAddress)
|
||||
End Sub
|
||||
|
||||
''' <summary>
|
||||
''' Gibt eine gemeinsame ChannelFactory zurück.
|
||||
''' Ein Connection-Pool für alle Client-Instanzen desselben Endpoints.
|
||||
''' </summary>
|
||||
Private Shared Function GetOrCreateSharedFactory(pLogConfig As LogConfig, pServerAddress As ServerAddressStruct) As ChannelFactory(Of IEDMIServiceChannel)
|
||||
Dim oEndpoint = $"net.tcp://{pServerAddress.Host}:{pServerAddress.Port}/DigitalData/Services/Main"
|
||||
|
||||
' ← Volatile.Read: garantiert aktuellen Wert auch ohne Lock
|
||||
Dim oCurrentFactory = Volatile.Read(_SharedFactory)
|
||||
If oCurrentFactory IsNot Nothing AndAlso
|
||||
_SharedFactoryEndpoint = oEndpoint AndAlso
|
||||
(oCurrentFactory.State = CommunicationState.Opened OrElse
|
||||
oCurrentFactory.State = CommunicationState.Created) Then
|
||||
Return oCurrentFactory
|
||||
End If
|
||||
|
||||
SyncLock _FactoryLock
|
||||
' Double-check nach Lock
|
||||
If _SharedFactory IsNot Nothing AndAlso
|
||||
_SharedFactoryEndpoint = oEndpoint AndAlso
|
||||
(_SharedFactory.State = CommunicationState.Opened OrElse
|
||||
_SharedFactory.State = CommunicationState.Created) Then
|
||||
Return _SharedFactory
|
||||
End If
|
||||
|
||||
If _SharedFactory IsNot Nothing Then
|
||||
If _SharedFactory.State = CommunicationState.Faulted OrElse
|
||||
_SharedFactory.State = CommunicationState.Closing Then
|
||||
Try
|
||||
_SharedFactory.Abort()
|
||||
Catch
|
||||
End Try
|
||||
End If
|
||||
_SharedFactory = Nothing
|
||||
End If
|
||||
|
||||
Dim oLogger = pLogConfig.GetLogger()
|
||||
Dim oBinding = GetBinding()
|
||||
Dim oAddress = New EndpointAddress(oEndpoint)
|
||||
|
||||
oLogger.Debug("Creating shared ChannelFactory | Endpoint=[{0}] | MaxConnections=[{1}] | SendTimeout=[{2}] | OpenTimeout=[{3}]",
|
||||
oEndpoint, oBinding.MaxConnections, oBinding.SendTimeout, oBinding.OpenTimeout)
|
||||
|
||||
_SharedFactory = New ChannelFactory(Of IEDMIServiceChannel)(oBinding, oAddress)
|
||||
_SharedFactoryEndpoint = oEndpoint
|
||||
Return _SharedFactory
|
||||
End SyncLock
|
||||
End Function
|
||||
|
||||
''' <summary>
|
||||
''' Closes a channel gracefully, falls back to Abort on error
|
||||
''' </summary>
|
||||
Public Shared Sub CloseChannel(pChannel As IEDMIServiceChannel)
|
||||
If pChannel Is Nothing Then Return
|
||||
|
||||
Try
|
||||
If pChannel.State = CommunicationState.Opened Then
|
||||
pChannel.Close()
|
||||
@@ -61,31 +116,27 @@ Public Class Channel
|
||||
pChannel.State = CommunicationState.Closing Then
|
||||
pChannel.Abort()
|
||||
End If
|
||||
' CommunicationState.Created / Closed → nichts tun, Zähler nicht betroffen
|
||||
Catch ex As Exception
|
||||
Try
|
||||
pChannel.Abort()
|
||||
Catch
|
||||
' Ignore
|
||||
End Try
|
||||
End Try
|
||||
End Sub
|
||||
|
||||
''' <summary>
|
||||
''' Creates a channel and adds a Faulted-Handler
|
||||
''' </summary>
|
||||
''' <returns>A channel object</returns>
|
||||
Public Function GetChannel() As IEDMIServiceChannel
|
||||
Try
|
||||
Logger.Debug("Creating channel.")
|
||||
Logger.Debug("Creating channel from shared factory.")
|
||||
Dim oChannel = ChannelFactory.CreateChannel()
|
||||
|
||||
' Verbindung zählen beim Öffnen
|
||||
AddHandler oChannel.Opened, Sub()
|
||||
Dim oCount = Interlocked.Increment(_ActiveConnections)
|
||||
Logger.Debug("Channel opened. Active connections: [{0}]", oCount)
|
||||
End Sub
|
||||
|
||||
' Verbindung abziehen beim Schließen oder Fehler
|
||||
AddHandler oChannel.Closed, Sub()
|
||||
Dim oCount = Interlocked.Decrement(_ActiveConnections)
|
||||
Logger.Debug("Channel closed. Active connections: [{0}]", oCount)
|
||||
@@ -93,7 +144,7 @@ Public Class Channel
|
||||
|
||||
AddHandler oChannel.Faulted, Sub()
|
||||
Dim oCount = Interlocked.Decrement(_ActiveConnections)
|
||||
Logger.Warn("Channel faulted. Active connections: [{0}]", oCount)
|
||||
Logger.Warn("⚠ Channel faulted. Active connections: [{0}]", oCount)
|
||||
RaiseEvent Reconnect(Me, Nothing)
|
||||
End Sub
|
||||
|
||||
@@ -104,20 +155,4 @@ Public Class Channel
|
||||
Throw
|
||||
End Try
|
||||
End Function
|
||||
|
||||
Private Function GetChannelFactory(pServerAddress As ServerAddressStruct) As ChannelFactory(Of IEDMIServiceChannel)
|
||||
Dim oBinding = GetBinding()
|
||||
Dim oAddress = New EndpointAddress($"net.tcp://{pServerAddress.Host}:{pServerAddress.Port}/DigitalData/Services/Main")
|
||||
Logger.Debug("Creating ChannelFactory with Binding: SendTimeout=[{0}], ReceiveTimeout=[{1}], MaxConnections=[{2}], MaxBufferSize=[{3}], TransferMode=[{4}]",
|
||||
oBinding.SendTimeout,
|
||||
oBinding.ReceiveTimeout,
|
||||
oBinding.MaxConnections,
|
||||
oBinding.MaxBufferSize,
|
||||
oBinding.TransferMode)
|
||||
Dim oFactory = New ChannelFactory(Of IEDMIServiceChannel)(oBinding, oAddress)
|
||||
Return oFactory
|
||||
End Function
|
||||
|
||||
End Class
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ReferenceGroup xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" ID="1b516c6d-d2d0-4dfc-9557-3dd0593fa624" xmlns="urn:schemas-microsoft-com:xml-wcfservicemap">
|
||||
<ReferenceGroup xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ID="1b516c6d-d2d0-4dfc-9557-3dd0593fa624" xmlns="urn:schemas-microsoft-com:xml-wcfservicemap">
|
||||
<ClientOptions>
|
||||
<GenerateAsynchronousMethods>false</GenerateAsynchronousMethods>
|
||||
<GenerateTaskBasedAsynchronousMethod>true</GenerateTaskBasedAsynchronousMethod>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configurationSnapshot xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="urn:schemas-microsoft-com:xml-wcfconfigurationsnapshot">
|
||||
<configurationSnapshot xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:schemas-microsoft-com:xml-wcfconfigurationsnapshot">
|
||||
<behaviors />
|
||||
<bindings>
|
||||
<binding digest="System.ServiceModel.Configuration.NetTcpBindingElement, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089:<?xml version="1.0" encoding="utf-16"?><Data name="NetTcpBinding_IEDMIService" transferMode="Streamed"><security><transport sslProtocols="None" /></security></Data>" bindingType="netTcpBinding" name="NetTcpBinding_IEDMIService" />
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<SavedWcfConfigurationInformation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" Version="9.1" CheckSum="LUfq4Z7qL259JRuSf1xmDS/kLlBkZTtgk2vJyQH0Jvo=">
|
||||
<SavedWcfConfigurationInformation xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Version="9.1" CheckSum="4uBt9Y5m0Ue4fIPG6eooT5TT5MJ97hiEcthL6+tUJHw=">
|
||||
<bindingConfigurations>
|
||||
<bindingConfiguration bindingType="netTcpBinding" name="NetTcpBinding_IEDMIService">
|
||||
<properties>
|
||||
|
||||
@@ -342,14 +342,17 @@ Public Class DatabaseWithFallback
|
||||
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)
|
||||
_Logger.Debug("GetDatatableFromServiceAsync called | SQL=[{0}] | DB=[{1}] | ConnId=[{2}]",
|
||||
pSQLCommand, DatabaseType.ToString, pConnectionId)
|
||||
|
||||
If _Client Is Nothing OrElse _ClientConfig?.ForceDirectDatabaseAccess Then
|
||||
Return Await Task.Run(Function() GetDatatableFromDatabase(pSQLCommand, DatabaseType, pConnectionId))
|
||||
' ← Kein Task.Run: direkt synchron, Thread ist ohnehin frei
|
||||
Return GetDatatableFromDatabase(pSQLCommand, DatabaseType, pConnectionId)
|
||||
End If
|
||||
|
||||
Dim oResult As GetDatatableResponse = Nothing
|
||||
Try
|
||||
Dim oResult As GetDatatableResponse = Nothing
|
||||
|
||||
Select Case DatabaseType
|
||||
Case Constants.DatabaseType.ECM
|
||||
oResult = Await _Client.GetDatatableFromECMAsync(pSQLCommand)
|
||||
@@ -360,20 +363,22 @@ Public Class DatabaseWithFallback
|
||||
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))
|
||||
_Logger.Warn("⚠ GetDatatableFromServiceAsync failed | Fallback to DB | Error=[{0}]",
|
||||
If(oResult Is Nothing, "null", oResult.ErrorMessage))
|
||||
Return GetDatatableFromDatabase(pSQLCommand, DatabaseType, pConnectionId)
|
||||
End If
|
||||
|
||||
Return oResult.Table
|
||||
|
||||
Catch ex As InvalidOperationException
|
||||
_Logger.Warn("⚠ GetDatatableFromServiceAsync offline: [{0}] | Fallback to DB", ex.Message)
|
||||
Return GetDatatableFromDatabase(pSQLCommand, DatabaseType, pConnectionId)
|
||||
|
||||
Catch ex As Exception
|
||||
_Logger.Warn("⚠ GetDatatableFromServiceAsync failed | Fallback to DB")
|
||||
_Logger.Error(ex)
|
||||
Return GetDatatableFromDatabase(pSQLCommand, DatabaseType, pConnectionId)
|
||||
End Try
|
||||
End Function
|
||||
Private Function GetDatatableFromDatabase(pSQLCommand As String, pDatabaseType As Constants.DatabaseType, pConnectionId As Integer) As DataTable
|
||||
Try
|
||||
@@ -401,16 +406,16 @@ 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)
|
||||
_Logger.Debug("GetScalarValueFromServiceAsync called | SQL=[{0}] | DB=[{1}] | ConnId=[{2}]",
|
||||
pSQLCommand, DatabaseType.ToString, pConnectionId)
|
||||
|
||||
If _Client Is Nothing OrElse _ClientConfig?.ForceDirectDatabaseAccess Then
|
||||
Return Await Task.Run(Function() GetScalarValueFromDatabase(pSQLCommand, DatabaseType, pConnectionId))
|
||||
Return GetScalarValueFromDatabase(pSQLCommand, DatabaseType, pConnectionId) ' ← kein Task.Run
|
||||
End If
|
||||
|
||||
Dim oResult As GetScalarValueResponse = Nothing
|
||||
Try
|
||||
Dim oResult As GetScalarValueResponse = Nothing
|
||||
|
||||
Select Case DatabaseType
|
||||
Case Constants.DatabaseType.ECM
|
||||
oResult = Await _Client.GetScalarValueFromECMAsync(pSQLCommand)
|
||||
@@ -421,20 +426,21 @@ Public Class DatabaseWithFallback
|
||||
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))
|
||||
_Logger.Warn("⚠ GetScalarValueFromServiceAsync failed | Fallback to DB")
|
||||
Return GetScalarValueFromDatabase(pSQLCommand, DatabaseType, pConnectionId)
|
||||
End If
|
||||
|
||||
Return oResult.Scalar
|
||||
|
||||
Catch ex As InvalidOperationException
|
||||
_Logger.Warn("⚠ GetScalarValueFromServiceAsync offline: [{0}] | Fallback to DB", ex.Message)
|
||||
Return GetScalarValueFromDatabase(pSQLCommand, DatabaseType, pConnectionId)
|
||||
|
||||
Catch ex As Exception
|
||||
_Logger.Warn("⚠ GetScalarValueFromServiceAsync failed | Fallback to DB")
|
||||
_Logger.Error(ex)
|
||||
Return GetScalarValueFromDatabase(pSQLCommand, DatabaseType, pConnectionId)
|
||||
End Try
|
||||
End Function
|
||||
Private Function GetScalarValueFromService(pSQLCommand As String, DatabaseType As Constants.DatabaseType, pConnectionId As Integer) As Object
|
||||
Try
|
||||
@@ -493,16 +499,16 @@ 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)
|
||||
_Logger.Debug("ExecuteNonQueryFromServiceAsync called | SQL=[{0}] | DB=[{1}] | ConnId=[{2}]",
|
||||
pSQLCommand, DatabaseType.ToString, pConnectionId)
|
||||
|
||||
If _Client Is Nothing OrElse _ClientConfig?.ForceDirectDatabaseAccess Then
|
||||
Return Await Task.Run(Function() ExecuteNonQueryFromDatabase(pSQLCommand, DatabaseType, pConnectionId))
|
||||
Return ExecuteNonQueryFromDatabase(pSQLCommand, DatabaseType, pConnectionId) ' ← kein Task.Run
|
||||
End If
|
||||
|
||||
Dim oResult As ExecuteNonQueryResponse = Nothing
|
||||
Try
|
||||
Dim oResult As ExecuteNonQueryResponse = Nothing
|
||||
|
||||
Select Case DatabaseType
|
||||
Case Constants.DatabaseType.ECM
|
||||
oResult = Await _Client.ExecuteNonQueryFromECMAsync(pSQLCommand)
|
||||
@@ -513,20 +519,21 @@ Public Class DatabaseWithFallback
|
||||
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))
|
||||
_Logger.Warn("⚠ ExecuteNonQueryFromServiceAsync failed | Fallback to DB")
|
||||
Return ExecuteNonQueryFromDatabase(pSQLCommand, DatabaseType, pConnectionId)
|
||||
End If
|
||||
|
||||
Return oResult.Result
|
||||
|
||||
Catch ex As InvalidOperationException
|
||||
_Logger.Warn("⚠ ExecuteNonQueryFromServiceAsync offline: [{0}] | Fallback to DB", ex.Message)
|
||||
Return ExecuteNonQueryFromDatabase(pSQLCommand, DatabaseType, pConnectionId)
|
||||
|
||||
Catch ex As Exception
|
||||
_Logger.Warn("⚠ ExecuteNonQueryFromServiceAsync failed | Fallback to DB")
|
||||
_Logger.Error(ex)
|
||||
Return ExecuteNonQueryFromDatabase(pSQLCommand, DatabaseType, pConnectionId)
|
||||
End Try
|
||||
End Function
|
||||
Private Function ExecuteNonQueryFromService(pSQLCommand As String, DatabaseType As Constants.DatabaseType, pConnectionId As Integer) As Boolean
|
||||
Try
|
||||
|
||||
@@ -13,7 +13,7 @@ Imports System.Runtime.InteropServices
|
||||
<Assembly: AssemblyCompany("Digital Data")>
|
||||
<Assembly: AssemblyProduct("EDMIAPI")>
|
||||
<Assembly: AssemblyCopyright("Copyright © 2026")>
|
||||
<Assembly: AssemblyTrademark("2.7.0")>
|
||||
<Assembly: AssemblyTrademark("2.8.0")>
|
||||
|
||||
<Assembly: ComVisible(False)>
|
||||
|
||||
@@ -31,5 +31,5 @@ Imports System.Runtime.InteropServices
|
||||
' übernehmen, indem Sie "*" eingeben:
|
||||
' <Assembly: AssemblyVersion("1.0.*")>
|
||||
|
||||
<Assembly: AssemblyVersion("2.7.0.0")>
|
||||
<Assembly: AssemblyVersion("2.8.0.0")>
|
||||
<Assembly: AssemblyFileVersion("1.6.1.1")>
|
||||
|
||||
3401
EDMIAPI/Reference.vb
Normal file
3401
EDMIAPI/Reference.vb
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user