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
|
' Runtime Variables
|
||||||
Private _ServerAddress As ServerAddressStruct
|
Private _ServerAddress As ServerAddressStruct
|
||||||
Private _ClientConfig As GlobalStateClientConfiguration
|
Private _ClientConfig As GlobalStateClientConfiguration
|
||||||
|
|
||||||
|
Private ReadOnly _CachedTablesLock As New ReaderWriterLockSlim()
|
||||||
Private _CachedTables As New List(Of String)
|
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)
|
' Verhindert parallele Reconnect-Versuche (0 = frei, 1 = läuft)
|
||||||
Private _IsReconnecting As Integer = 0
|
Private _IsReconnecting As Integer = 0
|
||||||
Private _Channel As IEDMIServiceChannel
|
Private _Channel As IEDMIServiceChannel
|
||||||
@@ -56,14 +93,14 @@ Public Class Client
|
|||||||
Protected Overridable Sub Dispose(disposing As Boolean)
|
Protected Overridable Sub Dispose(disposing As Boolean)
|
||||||
If _Disposed Then Return
|
If _Disposed Then Return
|
||||||
If disposing Then
|
If disposing Then
|
||||||
_IsOnline = False ' ← Reconnect-Guard deaktivieren
|
SetOffline()
|
||||||
UpdateTimer.Stop()
|
UpdateTimer.Stop()
|
||||||
UpdateTimer.Dispose()
|
UpdateTimer.Dispose()
|
||||||
|
|
||||||
' Auf laufenden Reconnect warten
|
' Auf laufenden Reconnect warten
|
||||||
Dim oTimeout = DateTime.Now.AddSeconds(5)
|
Dim oTimeout = DateTime.Now.AddSeconds(5)
|
||||||
While _IsReconnecting = 1 AndAlso DateTime.Now < oTimeout
|
While _IsReconnecting = 1 AndAlso DateTime.Now < oTimeout
|
||||||
Task.Delay(100).Wait()
|
Thread.Sleep(100)
|
||||||
End While
|
End While
|
||||||
|
|
||||||
If _Channel IsNot Nothing Then
|
If _Channel IsNot Nothing Then
|
||||||
@@ -71,6 +108,8 @@ Public Class Client
|
|||||||
_Channel = Nothing
|
_Channel = Nothing
|
||||||
End If
|
End If
|
||||||
|
|
||||||
|
_CachedTablesLock.Dispose() ' ← NEU: Lock freigeben
|
||||||
|
|
||||||
Dim oRemoved As ClientStatus = Nothing
|
Dim oRemoved As ClientStatus = Nothing
|
||||||
_ActiveClients.TryRemove(_ClientId, oRemoved)
|
_ActiveClients.TryRemove(_ClientId, oRemoved)
|
||||||
Logger.Debug("Client [{0}] unregistered. Total active clients: [{1}]", _ClientName, _ActiveClients.Count)
|
Logger.Debug("Client [{0}] unregistered. Total active clients: [{1}]", _ClientName, _ActiveClients.Count)
|
||||||
@@ -79,11 +118,6 @@ Public Class Client
|
|||||||
End Sub
|
End Sub
|
||||||
|
|
||||||
' Public Variables
|
' Public Variables
|
||||||
Public ReadOnly Property CachedTables
|
|
||||||
Get
|
|
||||||
Return _CachedTables
|
|
||||||
End Get
|
|
||||||
End Property
|
|
||||||
|
|
||||||
Public ReadOnly Property ClientConfig As GlobalStateClientConfiguration
|
Public ReadOnly Property ClientConfig As GlobalStateClientConfiguration
|
||||||
Get
|
Get
|
||||||
@@ -94,13 +128,6 @@ Public Class Client
|
|||||||
Return _ClientConfig
|
Return _ClientConfig
|
||||||
End Get
|
End Get
|
||||||
End Property
|
End Property
|
||||||
|
|
||||||
Public ReadOnly Property IsOnline As Boolean
|
|
||||||
Get
|
|
||||||
Return _IsOnline
|
|
||||||
End Get
|
|
||||||
End Property
|
|
||||||
|
|
||||||
Public ReadOnly Property ServerAddress As String
|
Public ReadOnly Property ServerAddress As String
|
||||||
Get
|
Get
|
||||||
Return $"{_ServerAddress.Host}:{_ServerAddress.Port}"
|
Return $"{_ServerAddress.Host}:{_ServerAddress.Port}"
|
||||||
@@ -197,14 +224,14 @@ Public Class Client
|
|||||||
' CachedTables sofort befüllen – nicht auf den ersten Timer-Tick warten
|
' CachedTables sofort befüllen – nicht auf den ersten Timer-Tick warten
|
||||||
Try
|
Try
|
||||||
Dim oTables As String() = _Channel.GetCachedTables()
|
Dim oTables As String() = _Channel.GetCachedTables()
|
||||||
_CachedTables = oTables.Select(Function(t) t.ToUpper).ToList()
|
SetCachedTables(oTables.Select(Function(t) t.ToUpper()))
|
||||||
Logger.Debug("✔ CachedTables loaded on connect: [{0}]", _CachedTables.Count)
|
Logger.Debug("✔ CachedTables loaded on connect: [{0}]", CachedTables.Count)
|
||||||
Catch ex As Exception
|
Catch ex As Exception
|
||||||
Logger.Warn("⚠ CachedTables could not be loaded on connect: {0}", ex.Message)
|
Logger.Warn("⚠ CachedTables could not be loaded on connect: {0}", ex.Message)
|
||||||
_CachedTables = New List(Of String)
|
SetCachedTables(Enumerable.Empty(Of String)())
|
||||||
End Try
|
End Try
|
||||||
|
|
||||||
_IsOnline = True ' ← vor GetStatus()
|
SetOnline()
|
||||||
UpdateTimer.Start()
|
UpdateTimer.Start()
|
||||||
|
|
||||||
' In Registry eintragen – jetzt mit korrektem IsOnline=True
|
' In Registry eintragen – jetzt mit korrektem IsOnline=True
|
||||||
@@ -213,7 +240,7 @@ Public Class Client
|
|||||||
|
|
||||||
Return True
|
Return True
|
||||||
Catch ex As Exception
|
Catch ex As Exception
|
||||||
_IsOnline = False
|
SetOffline()
|
||||||
Logger.Error(ex)
|
Logger.Error(ex)
|
||||||
Return False
|
Return False
|
||||||
End Try
|
End Try
|
||||||
@@ -243,7 +270,7 @@ Public Class Client
|
|||||||
_Channel = ChannelManager.GetChannel()
|
_Channel = ChannelManager.GetChannel()
|
||||||
_Channel.Open()
|
_Channel.Open()
|
||||||
|
|
||||||
_IsOnline = True
|
SetOnline()
|
||||||
|
|
||||||
If _ActiveClients.ContainsKey(_ClientId) Then
|
If _ActiveClients.ContainsKey(_ClientId) Then
|
||||||
_ActiveClients(_ClientId) = GetStatus()
|
_ActiveClients(_ClientId) = GetStatus()
|
||||||
@@ -253,7 +280,7 @@ Public Class Client
|
|||||||
Return
|
Return
|
||||||
Catch ex As Exception
|
Catch ex As Exception
|
||||||
Logger.Warn("⚠ Reconnect failed after [{0}]s: {1}", oDelay, ex.Message)
|
Logger.Warn("⚠ Reconnect failed after [{0}]s: {1}", oDelay, ex.Message)
|
||||||
_IsOnline = False
|
SetOffline()
|
||||||
End Try
|
End Try
|
||||||
Next
|
Next
|
||||||
|
|
||||||
@@ -266,53 +293,51 @@ Public Class Client
|
|||||||
|
|
||||||
Private Async Sub UpdateTimer_Elapsed(sender As Object, e As Timers.ElapsedEventArgs) Handles UpdateTimer.Elapsed
|
Private Async Sub UpdateTimer_Elapsed(sender As Object, e As Timers.ElapsedEventArgs) Handles UpdateTimer.Elapsed
|
||||||
Try
|
Try
|
||||||
If _Channel Is Nothing OrElse _Channel.State <> CommunicationState.Opened Then
|
' ← Snapshot VOR dem Await – schützt gegen _Channel = Nothing nach Resume
|
||||||
Logger.Warn("⚠ UpdateTimer skipped: Channel not in Opened state [{0}]", If(_Channel IsNot Nothing, _Channel.State.ToString(), "null"))
|
Dim oChannel = _Channel
|
||||||
_IsOnline = False
|
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)
|
Reconnect(sender, e)
|
||||||
Return
|
Return
|
||||||
End If
|
End If
|
||||||
|
|
||||||
If Not _IsOnline Then
|
If Not IsOnline Then
|
||||||
Logger.Debug("UpdateTimer skipped: Client is offline.")
|
Logger.Debug("UpdateTimer skipped: Client is offline.")
|
||||||
Return
|
Return
|
||||||
End If
|
End If
|
||||||
|
|
||||||
Dim oTables As String() = Await _Channel.GetCachedTablesAsync()
|
Dim oTables As String() = Await oChannel.GetCachedTablesAsync() ' ← oChannel statt _Channel
|
||||||
' ◄ NullReferenceException-Schutz: Service kann Nothing zurückgeben
|
|
||||||
If oTables Is Nothing Then
|
If oTables Is Nothing Then
|
||||||
Logger.Warn("⚠ UpdateTimer: GetCachedTablesAsync returned Nothing.")
|
Logger.Warn("⚠ UpdateTimer: GetCachedTablesAsync returned Nothing.")
|
||||||
_CachedTables = New List(Of String)
|
SetCachedTables(Enumerable.Empty(Of String)())
|
||||||
Return
|
Return
|
||||||
End If
|
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}]",
|
Logger.Info("✔ Channel state: [{0}] | Active connections: [{1}] | CachedTables: [{2}]",
|
||||||
_Channel.State,
|
oChannel.State, Channel.ActiveConnections, CachedTables.Count)
|
||||||
Channel.ActiveConnections,
|
|
||||||
_CachedTables.Count)
|
|
||||||
|
|
||||||
If _ActiveClients.ContainsKey(_ClientId) Then
|
If _ActiveClients.ContainsKey(_ClientId) Then
|
||||||
_ActiveClients(_ClientId) = GetStatus()
|
_ActiveClients(_ClientId) = GetStatus()
|
||||||
End If
|
End If
|
||||||
|
|
||||||
Catch ex As TimeoutException
|
Catch ex As TimeoutException
|
||||||
' Stale TCP connection (z.B. Firewall/Citrix Idle-Timeout)
|
Logger.Warn("⚠ UpdateTimer: TCP connection lost. Triggering reconnect...")
|
||||||
Logger.Warn("⚠ UpdateTimer: TCP connection lost (idle timeout / firewall). Triggering reconnect...")
|
SetOffline()
|
||||||
_IsOnline = False
|
|
||||||
Reconnect()
|
Reconnect()
|
||||||
|
|
||||||
Catch ex As CommunicationException
|
Catch ex As CommunicationException
|
||||||
Logger.Warn("⚠ UpdateTimer: Channel faulted. Triggering reconnect...")
|
Logger.Warn("⚠ UpdateTimer: Channel faulted. Triggering reconnect...")
|
||||||
_IsOnline = False
|
SetOffline()
|
||||||
Reconnect()
|
Reconnect()
|
||||||
|
|
||||||
Catch ex As Exception
|
Catch ex As Exception
|
||||||
Logger.Warn("⚠ Update of CachedTable was not successful!")
|
Logger.Warn("⚠ Update of CachedTable was not successful!")
|
||||||
Logger.Error(ex)
|
Logger.Error(ex)
|
||||||
_CachedTables = New List(Of String)
|
SetCachedTables(Enumerable.Empty(Of String)())
|
||||||
End Try
|
End Try
|
||||||
End Sub
|
End Sub
|
||||||
|
|
||||||
@@ -626,6 +651,59 @@ Public Class Client
|
|||||||
End Function
|
End Function
|
||||||
|
|
||||||
#Region "GetDatatable"
|
#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
|
Public Function GetDatatableFromIDB(pSQL As String) As GetDatatableResponse
|
||||||
EnsureAvailable()
|
EnsureAvailable()
|
||||||
Try
|
Try
|
||||||
@@ -633,6 +711,8 @@ Public Class Client
|
|||||||
.SqlCommand = pSQL,
|
.SqlCommand = pSQL,
|
||||||
.NamedDatabase = DatabaseName.IDB
|
.NamedDatabase = DatabaseName.IDB
|
||||||
})
|
})
|
||||||
|
Catch ex As TimeoutException ' ← NEU
|
||||||
|
HandleCommunicationError(ex, NameOf(GetDatatableFromIDB))
|
||||||
Catch ex As EndpointNotFoundException
|
Catch ex As EndpointNotFoundException
|
||||||
HandleCommunicationError(ex, NameOf(GetDatatableFromIDB))
|
HandleCommunicationError(ex, NameOf(GetDatatableFromIDB))
|
||||||
Catch ex As CommunicationException
|
Catch ex As CommunicationException
|
||||||
@@ -643,6 +723,7 @@ Public Class Client
|
|||||||
End Try
|
End Try
|
||||||
End Function
|
End Function
|
||||||
|
|
||||||
|
' ── GetDatatable ──────────────────────────────────────────
|
||||||
Public Function GetDatatableFromECM(pSQL As String) As GetDatatableResponse
|
Public Function GetDatatableFromECM(pSQL As String) As GetDatatableResponse
|
||||||
EnsureAvailable()
|
EnsureAvailable()
|
||||||
Try
|
Try
|
||||||
@@ -650,6 +731,8 @@ Public Class Client
|
|||||||
.SqlCommand = pSQL,
|
.SqlCommand = pSQL,
|
||||||
.NamedDatabase = DatabaseName.ECM
|
.NamedDatabase = DatabaseName.ECM
|
||||||
})
|
})
|
||||||
|
Catch ex As TimeoutException
|
||||||
|
HandleCommunicationError(ex, NameOf(GetDatatableFromECM))
|
||||||
Catch ex As EndpointNotFoundException
|
Catch ex As EndpointNotFoundException
|
||||||
HandleCommunicationError(ex, NameOf(GetDatatableFromECM))
|
HandleCommunicationError(ex, NameOf(GetDatatableFromECM))
|
||||||
Catch ex As CommunicationException
|
Catch ex As CommunicationException
|
||||||
@@ -668,6 +751,8 @@ Public Class Client
|
|||||||
.NamedDatabase = DatabaseName.None,
|
.NamedDatabase = DatabaseName.None,
|
||||||
.ConnectionId = pConnectionId
|
.ConnectionId = pConnectionId
|
||||||
})
|
})
|
||||||
|
Catch ex As TimeoutException
|
||||||
|
HandleCommunicationError(ex, NameOf(GetDatatableFromConnection))
|
||||||
Catch ex As EndpointNotFoundException
|
Catch ex As EndpointNotFoundException
|
||||||
HandleCommunicationError(ex, NameOf(GetDatatableFromConnection))
|
HandleCommunicationError(ex, NameOf(GetDatatableFromConnection))
|
||||||
Catch ex As CommunicationException
|
Catch ex As CommunicationException
|
||||||
@@ -685,6 +770,8 @@ Public Class Client
|
|||||||
.SqlCommand = pSQL,
|
.SqlCommand = pSQL,
|
||||||
.NamedDatabase = DatabaseName.IDB
|
.NamedDatabase = DatabaseName.IDB
|
||||||
})
|
})
|
||||||
|
Catch ex As TimeoutException
|
||||||
|
HandleCommunicationError(ex, NameOf(GetDatatableFromIDBAsync))
|
||||||
Catch ex As EndpointNotFoundException
|
Catch ex As EndpointNotFoundException
|
||||||
HandleCommunicationError(ex, NameOf(GetDatatableFromIDBAsync))
|
HandleCommunicationError(ex, NameOf(GetDatatableFromIDBAsync))
|
||||||
Catch ex As CommunicationException
|
Catch ex As CommunicationException
|
||||||
@@ -703,6 +790,8 @@ Public Class Client
|
|||||||
.SqlCommand = pSQL,
|
.SqlCommand = pSQL,
|
||||||
.NamedDatabase = DatabaseName.ECM
|
.NamedDatabase = DatabaseName.ECM
|
||||||
})
|
})
|
||||||
|
Catch ex As TimeoutException
|
||||||
|
HandleCommunicationError(ex, NameOf(GetDatatableFromECMAsync))
|
||||||
Catch ex As EndpointNotFoundException
|
Catch ex As EndpointNotFoundException
|
||||||
HandleCommunicationError(ex, NameOf(GetDatatableFromECMAsync))
|
HandleCommunicationError(ex, NameOf(GetDatatableFromECMAsync))
|
||||||
Catch ex As CommunicationException
|
Catch ex As CommunicationException
|
||||||
@@ -721,6 +810,8 @@ Public Class Client
|
|||||||
.NamedDatabase = DatabaseName.None,
|
.NamedDatabase = DatabaseName.None,
|
||||||
.ConnectionId = pConnectionId
|
.ConnectionId = pConnectionId
|
||||||
})
|
})
|
||||||
|
Catch ex As TimeoutException
|
||||||
|
HandleCommunicationError(ex, NameOf(GetDatatableFromConnectionAsync))
|
||||||
Catch ex As EndpointNotFoundException
|
Catch ex As EndpointNotFoundException
|
||||||
HandleCommunicationError(ex, NameOf(GetDatatableFromConnectionAsync))
|
HandleCommunicationError(ex, NameOf(GetDatatableFromConnectionAsync))
|
||||||
Catch ex As CommunicationException
|
Catch ex As CommunicationException
|
||||||
@@ -730,26 +821,8 @@ Public Class Client
|
|||||||
Throw
|
Throw
|
||||||
End Try
|
End Try
|
||||||
End Function
|
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
|
Public Function GetScalarValueFromECM(pSQL As String) As GetScalarValueResponse
|
||||||
EnsureAvailable()
|
EnsureAvailable()
|
||||||
Try
|
Try
|
||||||
@@ -757,6 +830,8 @@ Public Class Client
|
|||||||
.SqlCommand = pSQL,
|
.SqlCommand = pSQL,
|
||||||
.NamedDatabase = DatabaseName.ECM
|
.NamedDatabase = DatabaseName.ECM
|
||||||
})
|
})
|
||||||
|
Catch ex As TimeoutException
|
||||||
|
HandleCommunicationError(ex, NameOf(GetScalarValueFromECM))
|
||||||
Catch ex As EndpointNotFoundException
|
Catch ex As EndpointNotFoundException
|
||||||
HandleCommunicationError(ex, NameOf(GetScalarValueFromECM))
|
HandleCommunicationError(ex, NameOf(GetScalarValueFromECM))
|
||||||
Catch ex As CommunicationException
|
Catch ex As CommunicationException
|
||||||
@@ -775,6 +850,8 @@ Public Class Client
|
|||||||
.NamedDatabase = DatabaseName.None,
|
.NamedDatabase = DatabaseName.None,
|
||||||
.ConnectionId = pConnectionId
|
.ConnectionId = pConnectionId
|
||||||
})
|
})
|
||||||
|
Catch ex As TimeoutException
|
||||||
|
HandleCommunicationError(ex, NameOf(GetScalarValueFromConnection))
|
||||||
Catch ex As EndpointNotFoundException
|
Catch ex As EndpointNotFoundException
|
||||||
HandleCommunicationError(ex, NameOf(GetScalarValueFromConnection))
|
HandleCommunicationError(ex, NameOf(GetScalarValueFromConnection))
|
||||||
Catch ex As CommunicationException
|
Catch ex As CommunicationException
|
||||||
@@ -792,6 +869,8 @@ Public Class Client
|
|||||||
.SqlCommand = pSQL,
|
.SqlCommand = pSQL,
|
||||||
.NamedDatabase = DatabaseName.IDB
|
.NamedDatabase = DatabaseName.IDB
|
||||||
})
|
})
|
||||||
|
Catch ex As TimeoutException
|
||||||
|
HandleCommunicationError(ex, NameOf(GetScalarValueFromIDBAsync))
|
||||||
Catch ex As EndpointNotFoundException
|
Catch ex As EndpointNotFoundException
|
||||||
HandleCommunicationError(ex, NameOf(GetScalarValueFromIDBAsync))
|
HandleCommunicationError(ex, NameOf(GetScalarValueFromIDBAsync))
|
||||||
Catch ex As CommunicationException
|
Catch ex As CommunicationException
|
||||||
@@ -809,6 +888,8 @@ Public Class Client
|
|||||||
.SqlCommand = pSQL,
|
.SqlCommand = pSQL,
|
||||||
.NamedDatabase = DatabaseName.ECM
|
.NamedDatabase = DatabaseName.ECM
|
||||||
})
|
})
|
||||||
|
Catch ex As TimeoutException
|
||||||
|
HandleCommunicationError(ex, NameOf(GetScalarValueFromECMAsync))
|
||||||
Catch ex As EndpointNotFoundException
|
Catch ex As EndpointNotFoundException
|
||||||
HandleCommunicationError(ex, NameOf(GetScalarValueFromECMAsync))
|
HandleCommunicationError(ex, NameOf(GetScalarValueFromECMAsync))
|
||||||
Catch ex As CommunicationException
|
Catch ex As CommunicationException
|
||||||
@@ -827,6 +908,8 @@ Public Class Client
|
|||||||
.NamedDatabase = DatabaseName.None,
|
.NamedDatabase = DatabaseName.None,
|
||||||
.ConnectionId = pConnectionId
|
.ConnectionId = pConnectionId
|
||||||
})
|
})
|
||||||
|
Catch ex As TimeoutException
|
||||||
|
HandleCommunicationError(ex, NameOf(GetScalarValueFromConnectionAsync))
|
||||||
Catch ex As EndpointNotFoundException
|
Catch ex As EndpointNotFoundException
|
||||||
HandleCommunicationError(ex, NameOf(GetScalarValueFromConnectionAsync))
|
HandleCommunicationError(ex, NameOf(GetScalarValueFromConnectionAsync))
|
||||||
Catch ex As CommunicationException
|
Catch ex As CommunicationException
|
||||||
@@ -836,26 +919,8 @@ Public Class Client
|
|||||||
Throw
|
Throw
|
||||||
End Try
|
End Try
|
||||||
End Function
|
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
|
Public Function ExecuteNonQueryFromECM(pSQL As String) As ExecuteNonQueryResponse
|
||||||
EnsureAvailable()
|
EnsureAvailable()
|
||||||
Try
|
Try
|
||||||
@@ -863,6 +928,8 @@ Public Class Client
|
|||||||
.SqlCommand = pSQL,
|
.SqlCommand = pSQL,
|
||||||
.NamedDatabase = DatabaseName.ECM
|
.NamedDatabase = DatabaseName.ECM
|
||||||
})
|
})
|
||||||
|
Catch ex As TimeoutException
|
||||||
|
HandleCommunicationError(ex, NameOf(ExecuteNonQueryFromECM))
|
||||||
Catch ex As EndpointNotFoundException
|
Catch ex As EndpointNotFoundException
|
||||||
HandleCommunicationError(ex, NameOf(ExecuteNonQueryFromECM))
|
HandleCommunicationError(ex, NameOf(ExecuteNonQueryFromECM))
|
||||||
Catch ex As CommunicationException
|
Catch ex As CommunicationException
|
||||||
@@ -881,6 +948,8 @@ Public Class Client
|
|||||||
.NamedDatabase = DatabaseName.None,
|
.NamedDatabase = DatabaseName.None,
|
||||||
.ConnectionId = pConnectionId
|
.ConnectionId = pConnectionId
|
||||||
})
|
})
|
||||||
|
Catch ex As TimeoutException
|
||||||
|
HandleCommunicationError(ex, NameOf(ExecuteNonQueryFromConnection))
|
||||||
Catch ex As EndpointNotFoundException
|
Catch ex As EndpointNotFoundException
|
||||||
HandleCommunicationError(ex, NameOf(ExecuteNonQueryFromConnection))
|
HandleCommunicationError(ex, NameOf(ExecuteNonQueryFromConnection))
|
||||||
Catch ex As CommunicationException
|
Catch ex As CommunicationException
|
||||||
@@ -898,6 +967,8 @@ Public Class Client
|
|||||||
.SqlCommand = pSQL,
|
.SqlCommand = pSQL,
|
||||||
.NamedDatabase = DatabaseName.IDB
|
.NamedDatabase = DatabaseName.IDB
|
||||||
})
|
})
|
||||||
|
Catch ex As TimeoutException
|
||||||
|
HandleCommunicationError(ex, NameOf(ExecuteNonQueryFromIDBAsync))
|
||||||
Catch ex As EndpointNotFoundException
|
Catch ex As EndpointNotFoundException
|
||||||
HandleCommunicationError(ex, NameOf(ExecuteNonQueryFromIDBAsync))
|
HandleCommunicationError(ex, NameOf(ExecuteNonQueryFromIDBAsync))
|
||||||
Catch ex As CommunicationException
|
Catch ex As CommunicationException
|
||||||
@@ -915,6 +986,8 @@ Public Class Client
|
|||||||
.SqlCommand = pSQL,
|
.SqlCommand = pSQL,
|
||||||
.NamedDatabase = DatabaseName.ECM
|
.NamedDatabase = DatabaseName.ECM
|
||||||
})
|
})
|
||||||
|
Catch ex As TimeoutException
|
||||||
|
HandleCommunicationError(ex, NameOf(ExecuteNonQueryFromECMAsync))
|
||||||
Catch ex As EndpointNotFoundException
|
Catch ex As EndpointNotFoundException
|
||||||
HandleCommunicationError(ex, NameOf(ExecuteNonQueryFromECMAsync))
|
HandleCommunicationError(ex, NameOf(ExecuteNonQueryFromECMAsync))
|
||||||
Catch ex As CommunicationException
|
Catch ex As CommunicationException
|
||||||
@@ -933,6 +1006,8 @@ Public Class Client
|
|||||||
.NamedDatabase = DatabaseName.None,
|
.NamedDatabase = DatabaseName.None,
|
||||||
.ConnectionId = pConnectionId
|
.ConnectionId = pConnectionId
|
||||||
})
|
})
|
||||||
|
Catch ex As TimeoutException
|
||||||
|
HandleCommunicationError(ex, NameOf(ExecuteNonQueryFromConnectionAsync))
|
||||||
Catch ex As EndpointNotFoundException
|
Catch ex As EndpointNotFoundException
|
||||||
HandleCommunicationError(ex, NameOf(ExecuteNonQueryFromConnectionAsync))
|
HandleCommunicationError(ex, NameOf(ExecuteNonQueryFromConnectionAsync))
|
||||||
Catch ex As CommunicationException
|
Catch ex As CommunicationException
|
||||||
@@ -942,45 +1017,26 @@ Public Class Client
|
|||||||
Throw
|
Throw
|
||||||
End Try
|
End Try
|
||||||
End Function
|
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
|
#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
|
Public Function GetDocumentInfo(UserId As Long, ObjectId As Long) As DocumentInfo
|
||||||
Try
|
Try
|
||||||
Dim oResponse As DocumentInfoResponse = _Channel.GetFileInfoByObjectId(New DocumentInfoRequest With {
|
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.")
|
Throw New InvalidOperationException("Channel is not initialized. Call Connect() first.")
|
||||||
End If
|
End If
|
||||||
|
|
||||||
If Not _IsOnline Then
|
If Not IsOnline Then
|
||||||
' Kurze Grace-Period abwarten, damit Reconnect() seinen _IsReconnecting-Flag setzen kann
|
' Wenn Reconnect aktiv läuft: kurze Grace-Period (max 1s) statt 10s
|
||||||
Dim oGraceLimit = DateTime.Now.AddMilliseconds(600)
|
If _IsReconnecting = 1 Then
|
||||||
While DateTime.Now < oGraceLimit AndAlso _IsReconnecting = 0 AndAlso Not _IsOnline
|
Dim oGraceLimit = DateTime.Now.AddMilliseconds(1000)
|
||||||
Task.Delay(100).Wait()
|
While DateTime.Now < oGraceLimit AndAlso Not IsOnline
|
||||||
|
Thread.Sleep(100)
|
||||||
End While
|
End While
|
||||||
|
End If
|
||||||
|
|
||||||
' Jetzt auf aktiven Reconnect warten
|
' Danach sofort werfen – WCF-Timeout nicht weiter aufbrauchen
|
||||||
Dim oWaitLimit = DateTime.Now.AddSeconds(10)
|
If Not IsOnline Then
|
||||||
While Not _IsOnline AndAlso DateTime.Now < oWaitLimit AndAlso _IsReconnecting = 1
|
Throw New InvalidOperationException(
|
||||||
Task.Delay(500).Wait()
|
$"Client is offline. Reconnect in progress=[{_IsReconnecting = 1}].")
|
||||||
Logger.Debug("EnsureAvailable: Waiting for reconnect... Channel=[{0}]", _Channel.State.ToString())
|
|
||||||
End While
|
|
||||||
|
|
||||||
If Not _IsOnline Then
|
|
||||||
Throw New InvalidOperationException("Client is offline.")
|
|
||||||
End If
|
End If
|
||||||
End If
|
End If
|
||||||
|
|
||||||
If _Channel.State <> CommunicationState.Opened Then
|
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 If
|
||||||
End Sub
|
End Sub
|
||||||
''' <summary>
|
''' <summary>
|
||||||
@@ -1084,6 +1138,8 @@ Public Class Client
|
|||||||
''' </summary>
|
''' </summary>
|
||||||
Private Sub HandleCommunicationError(ex As Exception, callerName As String)
|
Private Sub HandleCommunicationError(ex As Exception, callerName As String)
|
||||||
Select Case True
|
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
|
Case TypeOf ex Is EndpointNotFoundException
|
||||||
Logger.Warn("⚠ Service not reachable (EndpointNotFoundException in [{0}]). Triggering reconnect...", callerName)
|
Logger.Warn("⚠ Service not reachable (EndpointNotFoundException in [{0}]). Triggering reconnect...", callerName)
|
||||||
Case TypeOf ex Is CommunicationException
|
Case TypeOf ex Is CommunicationException
|
||||||
@@ -1093,7 +1149,7 @@ Public Class Client
|
|||||||
Logger.Error(ex)
|
Logger.Error(ex)
|
||||||
End Select
|
End Select
|
||||||
|
|
||||||
_IsOnline = False
|
SetOffline()
|
||||||
Reconnect()
|
Reconnect()
|
||||||
Throw ex
|
Throw ex
|
||||||
End Sub
|
End Sub
|
||||||
@@ -1190,15 +1246,16 @@ Public Class Client
|
|||||||
''' Returns a detailed status snapshot of the current client state
|
''' Returns a detailed status snapshot of the current client state
|
||||||
''' </summary>
|
''' </summary>
|
||||||
Public Function GetStatus() As ClientStatus
|
Public Function GetStatus() As ClientStatus
|
||||||
|
Dim oTables = CachedTables ' ← Property nutzen: enthält Read-Lock + Snapshot
|
||||||
Return New ClientStatus() With {
|
Return New ClientStatus() With {
|
||||||
.IsOnline = _IsOnline,
|
.IsOnline = IsOnline, ' ← Property statt Feld
|
||||||
.ClientId = _ClientId, ' ← Neu
|
.ClientId = _ClientId,
|
||||||
.ClientName = _ClientName,
|
.ClientName = _ClientName,
|
||||||
.ServerAddress = Me.ServerAddress,
|
.ServerAddress = Me.ServerAddress,
|
||||||
.ChannelState = If(_Channel IsNot Nothing, _Channel.State.ToString(), "null"),
|
.ChannelState = If(_Channel IsNot Nothing, _Channel.State.ToString(), "null"),
|
||||||
.ActiveConnections = Channel.ActiveConnections,
|
.ActiveConnections = Channel.ActiveConnections,
|
||||||
.CachedTableCount = _CachedTables.Count,
|
.CachedTableCount = oTables.Count, ' ← aus Snapshot
|
||||||
.CachedTables = _CachedTables.ToList(),
|
.CachedTables = oTables, ' ← aus Snapshot
|
||||||
.HasClientConfig = _ClientConfig IsNot Nothing,
|
.HasClientConfig = _ClientConfig IsNot Nothing,
|
||||||
.IsDisposed = _Disposed,
|
.IsDisposed = _Disposed,
|
||||||
.TimerEnabled = UpdateTimer.Enabled,
|
.TimerEnabled = UpdateTimer.Enabled,
|
||||||
|
|||||||
@@ -8,10 +8,14 @@ Imports DigitalData.Modules.Logging
|
|||||||
Public Class Channel
|
Public Class Channel
|
||||||
Inherits BaseClass
|
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)
|
Private ReadOnly ChannelFactory As ChannelFactory(Of IEDMIServiceChannel)
|
||||||
|
|
||||||
Public Event Reconnect As EventHandler
|
Public Event Reconnect As EventHandler
|
||||||
' Verbindungszähler
|
|
||||||
Private Shared _ActiveConnections As Integer = 0
|
Private Shared _ActiveConnections As Integer = 0
|
||||||
Public Shared ReadOnly Property ActiveConnections As Integer
|
Public Shared ReadOnly Property ActiveConnections As Integer
|
||||||
Get
|
Get
|
||||||
@@ -20,16 +24,18 @@ Public Class Channel
|
|||||||
End Property
|
End Property
|
||||||
|
|
||||||
Public Shared Function GetBinding(Optional AuthenticationMode As TcpClientCredentialType = TcpClientCredentialType.Windows) As NetTcpBinding
|
Public Shared Function GetBinding(Optional AuthenticationMode As TcpClientCredentialType = TcpClientCredentialType.Windows) As NetTcpBinding
|
||||||
|
Dim oMaxConnections = Math.Max(500, Environment.ProcessorCount * 50)
|
||||||
|
|
||||||
Return New NetTcpBinding() With {
|
Return New NetTcpBinding() With {
|
||||||
.MaxReceivedMessageSize = Constants.ChannelSettings.MAX_RECEIVED_MESSAGE_SIZE,
|
.MaxReceivedMessageSize = Constants.ChannelSettings.MAX_RECEIVED_MESSAGE_SIZE,
|
||||||
.MaxBufferSize = Constants.ChannelSettings.MAX_BUFFER_SIZE,
|
.MaxBufferSize = Constants.ChannelSettings.MAX_BUFFER_SIZE,
|
||||||
.MaxBufferPoolSize = Constants.ChannelSettings.MAX_BUFFER_POOL_SIZE,
|
.MaxBufferPoolSize = Constants.ChannelSettings.MAX_BUFFER_POOL_SIZE,
|
||||||
.ListenBacklog = Constants.ChannelSettings.MAX_CONNECTIONS,
|
.ListenBacklog = oMaxConnections,
|
||||||
.MaxConnections = Constants.ChannelSettings.MAX_CONNECTIONS,
|
.MaxConnections = oMaxConnections,
|
||||||
.TransferMode = TransferMode.Streamed,
|
.TransferMode = TransferMode.Streamed,
|
||||||
.SendTimeout = TimeSpan.FromSeconds(30), ' ← War: 5min → Schnellere Fehlererkennung
|
.SendTimeout = TimeSpan.FromSeconds(60),
|
||||||
.ReceiveTimeout = TimeSpan.FromMinutes(10), ' Keepalive < Firewall-Timeout (3600s) ✓
|
.ReceiveTimeout = TimeSpan.FromMinutes(10),
|
||||||
.OpenTimeout = TimeSpan.FromSeconds(30), ' ← Neu: Verbindungsaufbau begrenzen
|
.OpenTimeout = TimeSpan.FromSeconds(60),
|
||||||
.CloseTimeout = TimeSpan.FromSeconds(30),
|
.CloseTimeout = TimeSpan.FromSeconds(30),
|
||||||
.Security = New NetTcpSecurity() With {
|
.Security = New NetTcpSecurity() With {
|
||||||
.Mode = SecurityMode.Transport,
|
.Mode = SecurityMode.Transport,
|
||||||
@@ -46,14 +52,63 @@ Public Class Channel
|
|||||||
|
|
||||||
Public Sub New(pLogConfig As LogConfig, pServerAddress As ServerAddressStruct)
|
Public Sub New(pLogConfig As LogConfig, pServerAddress As ServerAddressStruct)
|
||||||
MyBase.New(pLogConfig)
|
MyBase.New(pLogConfig)
|
||||||
ChannelFactory = GetChannelFactory(pServerAddress)
|
ChannelFactory = GetOrCreateSharedFactory(pLogConfig, pServerAddress)
|
||||||
End Sub
|
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>
|
''' <summary>
|
||||||
''' Closes a channel gracefully, falls back to Abort on error
|
''' Closes a channel gracefully, falls back to Abort on error
|
||||||
''' </summary>
|
''' </summary>
|
||||||
Public Shared Sub CloseChannel(pChannel As IEDMIServiceChannel)
|
Public Shared Sub CloseChannel(pChannel As IEDMIServiceChannel)
|
||||||
If pChannel Is Nothing Then Return
|
If pChannel Is Nothing Then Return
|
||||||
|
|
||||||
Try
|
Try
|
||||||
If pChannel.State = CommunicationState.Opened Then
|
If pChannel.State = CommunicationState.Opened Then
|
||||||
pChannel.Close()
|
pChannel.Close()
|
||||||
@@ -61,31 +116,27 @@ Public Class Channel
|
|||||||
pChannel.State = CommunicationState.Closing Then
|
pChannel.State = CommunicationState.Closing Then
|
||||||
pChannel.Abort()
|
pChannel.Abort()
|
||||||
End If
|
End If
|
||||||
' CommunicationState.Created / Closed → nichts tun, Zähler nicht betroffen
|
|
||||||
Catch ex As Exception
|
Catch ex As Exception
|
||||||
Try
|
Try
|
||||||
pChannel.Abort()
|
pChannel.Abort()
|
||||||
Catch
|
Catch
|
||||||
' Ignore
|
|
||||||
End Try
|
End Try
|
||||||
End Try
|
End Try
|
||||||
End Sub
|
End Sub
|
||||||
|
|
||||||
''' <summary>
|
''' <summary>
|
||||||
''' Creates a channel and adds a Faulted-Handler
|
''' Creates a channel and adds a Faulted-Handler
|
||||||
''' </summary>
|
''' </summary>
|
||||||
''' <returns>A channel object</returns>
|
|
||||||
Public Function GetChannel() As IEDMIServiceChannel
|
Public Function GetChannel() As IEDMIServiceChannel
|
||||||
Try
|
Try
|
||||||
Logger.Debug("Creating channel.")
|
Logger.Debug("Creating channel from shared factory.")
|
||||||
Dim oChannel = ChannelFactory.CreateChannel()
|
Dim oChannel = ChannelFactory.CreateChannel()
|
||||||
|
|
||||||
' Verbindung zählen beim Öffnen
|
|
||||||
AddHandler oChannel.Opened, Sub()
|
AddHandler oChannel.Opened, Sub()
|
||||||
Dim oCount = Interlocked.Increment(_ActiveConnections)
|
Dim oCount = Interlocked.Increment(_ActiveConnections)
|
||||||
Logger.Debug("Channel opened. Active connections: [{0}]", oCount)
|
Logger.Debug("Channel opened. Active connections: [{0}]", oCount)
|
||||||
End Sub
|
End Sub
|
||||||
|
|
||||||
' Verbindung abziehen beim Schließen oder Fehler
|
|
||||||
AddHandler oChannel.Closed, Sub()
|
AddHandler oChannel.Closed, Sub()
|
||||||
Dim oCount = Interlocked.Decrement(_ActiveConnections)
|
Dim oCount = Interlocked.Decrement(_ActiveConnections)
|
||||||
Logger.Debug("Channel closed. Active connections: [{0}]", oCount)
|
Logger.Debug("Channel closed. Active connections: [{0}]", oCount)
|
||||||
@@ -93,7 +144,7 @@ Public Class Channel
|
|||||||
|
|
||||||
AddHandler oChannel.Faulted, Sub()
|
AddHandler oChannel.Faulted, Sub()
|
||||||
Dim oCount = Interlocked.Decrement(_ActiveConnections)
|
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)
|
RaiseEvent Reconnect(Me, Nothing)
|
||||||
End Sub
|
End Sub
|
||||||
|
|
||||||
@@ -104,20 +155,4 @@ Public Class Channel
|
|||||||
Throw
|
Throw
|
||||||
End Try
|
End Try
|
||||||
End Function
|
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
|
End Class
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?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>
|
<ClientOptions>
|
||||||
<GenerateAsynchronousMethods>false</GenerateAsynchronousMethods>
|
<GenerateAsynchronousMethods>false</GenerateAsynchronousMethods>
|
||||||
<GenerateTaskBasedAsynchronousMethod>true</GenerateTaskBasedAsynchronousMethod>
|
<GenerateTaskBasedAsynchronousMethod>true</GenerateTaskBasedAsynchronousMethod>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?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 />
|
<behaviors />
|
||||||
<bindings>
|
<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" />
|
<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"?>
|
<?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>
|
<bindingConfigurations>
|
||||||
<bindingConfiguration bindingType="netTcpBinding" name="NetTcpBinding_IEDMIService">
|
<bindingConfiguration bindingType="netTcpBinding" name="NetTcpBinding_IEDMIService">
|
||||||
<properties>
|
<properties>
|
||||||
|
|||||||
@@ -342,14 +342,17 @@ Public Class DatabaseWithFallback
|
|||||||
End Function
|
End Function
|
||||||
|
|
||||||
Private Async Function GetDatatableFromServiceAsync(pSQLCommand As String, DatabaseType As Constants.DatabaseType, pConnectionId As Integer) As Task(Of DataTable)
|
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 called | SQL=[{0}] | DB=[{1}] | ConnId=[{2}]",
|
||||||
_Logger.Debug("GetDatatableFromServiceAsync: GetDatatableFromServiceAsync called with SQL [{0}], DatabaseType [{1}], ConnectionId [{2}]", pSQLCommand, DatabaseType.ToString, pConnectionId)
|
pSQLCommand, DatabaseType.ToString, pConnectionId)
|
||||||
|
|
||||||
If _Client Is Nothing OrElse _ClientConfig?.ForceDirectDatabaseAccess Then
|
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
|
End If
|
||||||
|
|
||||||
Dim oResult As GetDatatableResponse = Nothing
|
|
||||||
Try
|
Try
|
||||||
|
Dim oResult As GetDatatableResponse = Nothing
|
||||||
|
|
||||||
Select Case DatabaseType
|
Select Case DatabaseType
|
||||||
Case Constants.DatabaseType.ECM
|
Case Constants.DatabaseType.ECM
|
||||||
oResult = Await _Client.GetDatatableFromECMAsync(pSQLCommand)
|
oResult = Await _Client.GetDatatableFromECMAsync(pSQLCommand)
|
||||||
@@ -360,20 +363,22 @@ Public Class DatabaseWithFallback
|
|||||||
End Select
|
End Select
|
||||||
|
|
||||||
If oResult Is Nothing OrElse oResult.OK = False Then
|
If oResult Is Nothing OrElse oResult.OK = False Then
|
||||||
_Logger.Warn("⚠ GetDatatableFromServiceAsync failed. Falling back to direct database access.")
|
_Logger.Warn("⚠ GetDatatableFromServiceAsync failed | Fallback to DB | Error=[{0}]",
|
||||||
oFallback = True
|
If(oResult Is Nothing, "null", oResult.ErrorMessage))
|
||||||
End If
|
Return GetDatatableFromDatabase(pSQLCommand, DatabaseType, pConnectionId)
|
||||||
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
|
End If
|
||||||
|
|
||||||
Return oResult.Table
|
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
|
End Function
|
||||||
Private Function GetDatatableFromDatabase(pSQLCommand As String, pDatabaseType As Constants.DatabaseType, pConnectionId As Integer) As DataTable
|
Private Function GetDatatableFromDatabase(pSQLCommand As String, pDatabaseType As Constants.DatabaseType, pConnectionId As Integer) As DataTable
|
||||||
Try
|
Try
|
||||||
@@ -401,16 +406,16 @@ Public Class DatabaseWithFallback
|
|||||||
End Try
|
End Try
|
||||||
End Function
|
End Function
|
||||||
Private Async Function GetScalarValueFromServiceAsync(pSQLCommand As String, DatabaseType As Constants.DatabaseType, pConnectionId As Integer) As Task(Of Object)
|
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 | SQL=[{0}] | DB=[{1}] | ConnId=[{2}]",
|
||||||
|
pSQLCommand, DatabaseType.ToString, pConnectionId)
|
||||||
_Logger.Debug("GetScalarValueFromServiceAsync called with SQL [{0}], DatabaseType [{1}], ConnectionId [{2}]", pSQLCommand, DatabaseType.ToString, pConnectionId)
|
|
||||||
|
|
||||||
If _Client Is Nothing OrElse _ClientConfig?.ForceDirectDatabaseAccess Then
|
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
|
End If
|
||||||
|
|
||||||
Dim oResult As GetScalarValueResponse = Nothing
|
|
||||||
Try
|
Try
|
||||||
|
Dim oResult As GetScalarValueResponse = Nothing
|
||||||
|
|
||||||
Select Case DatabaseType
|
Select Case DatabaseType
|
||||||
Case Constants.DatabaseType.ECM
|
Case Constants.DatabaseType.ECM
|
||||||
oResult = Await _Client.GetScalarValueFromECMAsync(pSQLCommand)
|
oResult = Await _Client.GetScalarValueFromECMAsync(pSQLCommand)
|
||||||
@@ -421,20 +426,21 @@ Public Class DatabaseWithFallback
|
|||||||
End Select
|
End Select
|
||||||
|
|
||||||
If oResult Is Nothing OrElse oResult.OK = False Then
|
If oResult Is Nothing OrElse oResult.OK = False Then
|
||||||
_Logger.Warn("⚠ GetScalarValueFromServiceAsync failed. Falling back to direct database access.")
|
_Logger.Warn("⚠ GetScalarValueFromServiceAsync failed | Fallback to DB")
|
||||||
oFallback = True
|
Return GetScalarValueFromDatabase(pSQLCommand, DatabaseType, pConnectionId)
|
||||||
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
|
End If
|
||||||
|
|
||||||
Return oResult.Scalar
|
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
|
End Function
|
||||||
Private Function GetScalarValueFromService(pSQLCommand As String, DatabaseType As Constants.DatabaseType, pConnectionId As Integer) As Object
|
Private Function GetScalarValueFromService(pSQLCommand As String, DatabaseType As Constants.DatabaseType, pConnectionId As Integer) As Object
|
||||||
Try
|
Try
|
||||||
@@ -493,16 +499,16 @@ Public Class DatabaseWithFallback
|
|||||||
End Try
|
End Try
|
||||||
End Function
|
End Function
|
||||||
Private Async Function ExecuteNonQueryFromServiceAsync(pSQLCommand As String, DatabaseType As Constants.DatabaseType, pConnectionId As Integer) As Task(Of Boolean)
|
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 | SQL=[{0}] | DB=[{1}] | ConnId=[{2}]",
|
||||||
|
pSQLCommand, DatabaseType.ToString, pConnectionId)
|
||||||
_Logger.Debug("ExecuteNonQueryFromServiceAsync called with SQL [{0}], DatabaseType [{1}], ConnectionId [{2}]", pSQLCommand, DatabaseType.ToString, pConnectionId)
|
|
||||||
|
|
||||||
If _Client Is Nothing OrElse _ClientConfig?.ForceDirectDatabaseAccess Then
|
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
|
End If
|
||||||
|
|
||||||
Dim oResult As ExecuteNonQueryResponse = Nothing
|
|
||||||
Try
|
Try
|
||||||
|
Dim oResult As ExecuteNonQueryResponse = Nothing
|
||||||
|
|
||||||
Select Case DatabaseType
|
Select Case DatabaseType
|
||||||
Case Constants.DatabaseType.ECM
|
Case Constants.DatabaseType.ECM
|
||||||
oResult = Await _Client.ExecuteNonQueryFromECMAsync(pSQLCommand)
|
oResult = Await _Client.ExecuteNonQueryFromECMAsync(pSQLCommand)
|
||||||
@@ -513,20 +519,21 @@ Public Class DatabaseWithFallback
|
|||||||
End Select
|
End Select
|
||||||
|
|
||||||
If oResult Is Nothing OrElse oResult.OK = False Then
|
If oResult Is Nothing OrElse oResult.OK = False Then
|
||||||
_Logger.Warn("⚠ ExecuteNonQueryFromServiceAsync failed. Falling back to direct database access.")
|
_Logger.Warn("⚠ ExecuteNonQueryFromServiceAsync failed | Fallback to DB")
|
||||||
oFallback = True
|
Return ExecuteNonQueryFromDatabase(pSQLCommand, DatabaseType, pConnectionId)
|
||||||
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
|
End If
|
||||||
|
|
||||||
Return oResult.Result
|
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
|
End Function
|
||||||
Private Function ExecuteNonQueryFromService(pSQLCommand As String, DatabaseType As Constants.DatabaseType, pConnectionId As Integer) As Boolean
|
Private Function ExecuteNonQueryFromService(pSQLCommand As String, DatabaseType As Constants.DatabaseType, pConnectionId As Integer) As Boolean
|
||||||
Try
|
Try
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ Imports System.Runtime.InteropServices
|
|||||||
<Assembly: AssemblyCompany("Digital Data")>
|
<Assembly: AssemblyCompany("Digital Data")>
|
||||||
<Assembly: AssemblyProduct("EDMIAPI")>
|
<Assembly: AssemblyProduct("EDMIAPI")>
|
||||||
<Assembly: AssemblyCopyright("Copyright © 2026")>
|
<Assembly: AssemblyCopyright("Copyright © 2026")>
|
||||||
<Assembly: AssemblyTrademark("2.7.0")>
|
<Assembly: AssemblyTrademark("2.8.0")>
|
||||||
|
|
||||||
<Assembly: ComVisible(False)>
|
<Assembly: ComVisible(False)>
|
||||||
|
|
||||||
@@ -31,5 +31,5 @@ Imports System.Runtime.InteropServices
|
|||||||
' übernehmen, indem Sie "*" eingeben:
|
' übernehmen, indem Sie "*" eingeben:
|
||||||
' <Assembly: AssemblyVersion("1.0.*")>
|
' <Assembly: AssemblyVersion("1.0.*")>
|
||||||
|
|
||||||
<Assembly: AssemblyVersion("2.7.0.0")>
|
<Assembly: AssemblyVersion("2.8.0.0")>
|
||||||
<Assembly: AssemblyFileVersion("1.6.1.1")>
|
<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