Files
Monorepo/Service.EDMIService/EDMIService.vb
Developer01 9fbcb97fe4 Optimierung von Performance und Skalierbarkeit
- Assembly-Versionen auf 2.2.0.0 und 2.8.0.0 aktualisiert.
- Verbesserte Steuerung von `ScheduleScrollUpdate` in SnapPanel.
- Erhöhte Serverkapazität für 300+ Benutzer in EDMIService.
- Einführung von Windows-Authentifizierungs-Caching.
- Neuer atomarer Tabellen-Caching-Mechanismus in EDMIService.
- Lock-Mechanismus in JobListener optimiert (Instanz-Lock).
- Verbesserte Performance durch kürzere Lock-Dauern in SaveDataTables.
- Logging und Snapshot-Veröffentlichung nach Job-Ausführung optimiert.
- Allgemeine Skalierungs- und Performance-Verbesserungen.
2026-10-12 10:01:58 +02:00

792 lines
34 KiB
VB.net
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
Imports System.Diagnostics
Imports System.IO
Imports System.ServiceModel
Imports System.ServiceModel.Description
Imports System.Threading
Imports DigitalData.Modules.Database
Imports DigitalData.Modules.EDMI
Imports DigitalData.Modules.EDMI.API.Rights
Imports DigitalData.Modules.Logging
Imports DigitalData.Services.EDMIService.Exceptions
Imports DigitalData.Services.EDMIService.Messages
Imports DigitalData.Services.EDMIService.Methods
Imports DigitalData.Services.EDMIService.Methods.Database
Imports DigitalData.Services.EDMIService.Methods.IDB
<ServiceBehavior(InstanceContextMode:=InstanceContextMode.PerSession,
ConcurrencyMode:=ConcurrencyMode.Multiple)>
Public Class EDMIService
Implements IEDMIService
Public Shared LogConfig As LogConfig
Public Shared MSSQL_ECM As MSSQLServer
Public Shared MSSQL_IDB As MSSQLServer
Public Shared Firebird As Firebird
Public Shared AppConfig As Config
Public Shared GlobalState As GlobalState
Public Shared Scheduler As Scheduler
Public Const TBIDB_DOC_INFO = "TBIDB_DOC_INFO"
Public Const TBIDB_ACCESSRIGHT = "TBIDB_ACCESSRIGHT"
' ── Aktive Sessions: SessionId → Username (thread-safe) ──────────────────
Private Shared ReadOnly _ActiveSessions As New Collections.Concurrent.ConcurrentDictionary(Of String, String)
Public Shared ReadOnly Property ActiveSessions As Integer
Get
Return _ActiveSessions.Count
End Get
End Property
Public Shared ReadOnly Property ActiveSessionUsers As IReadOnlyCollection(Of String)
Get
Return _ActiveSessions.Values.Distinct().OrderBy(Function(u) u).ToList().AsReadOnly()
End Get
End Property
' ─────────────────────────────────────────────────────────────────────────
Private ReadOnly _Logger As Logger
Private ReadOnly _Debug As Boolean = False
Private ReadOnly _Username As String
Private ReadOnly _IDBHelpers As IDB.Helpers
' ── Konfigurationswerte für späteres Logging in New() ────────────────────
Private Shared _ConfiguredEndpoint As String = String.Empty
Private Shared _ConfiguredMaxCalls As Integer = 0
Private Shared _ConfiguredMaxSessions As Integer = 0
' ─────────────────────────────────────────────────────────────────────────
Public Shared Sub Configure(Config As ServiceConfiguration)
Dim oBaseAddress = Config.BaseAddresses.Item(0)
Dim oBinding = API.Channel.GetBinding()
Dim oAddress = New EndpointAddress(oBaseAddress)
Dim oDescription = ContractDescription.GetContract(GetType(IEDMIService), GetType(EDMIService))
Dim oEndpoint As New ServiceEndpoint(oDescription, oBinding, oAddress)
Config.AddServiceEndpoint(oEndpoint)
Config.Description.Behaviors.Add(New ServiceDebugBehavior With {.IncludeExceptionDetailInFaults = True})
Dim oThrottling = Config.Description.Behaviors.Find(Of ServiceThrottlingBehavior)()
If oThrottling Is Nothing Then
oThrottling = New ServiceThrottlingBehavior()
Config.Description.Behaviors.Add(oThrottling)
End If
Dim oCpuCores As Integer = Environment.ProcessorCount
' ← Server-seitige Kapazität für 300+ User
oThrottling.MaxConcurrentCalls = Math.Max(400, 32 * oCpuCores)
oThrottling.MaxConcurrentSessions = Math.Max(600, 150 * oCpuCores)
oThrottling.MaxConcurrentInstances = Integer.MaxValue
' ← Server-seitiges Binding: toleriert langsamere Clients (alte DLL!)
Dim oNetTcpBinding = TryCast(oBinding, NetTcpBinding)
If oNetTcpBinding IsNot Nothing Then
' ← Konsistent mit Channel.GetBinding(): CPUs × 50, mind. 500
Dim oMaxConnections = Math.Max(500, oCpuCores * 50)
oNetTcpBinding.SendTimeout = TimeSpan.FromMinutes(5)
oNetTcpBinding.ReceiveTimeout = TimeSpan.FromMinutes(10)
oNetTcpBinding.OpenTimeout = TimeSpan.FromMinutes(2)
oNetTcpBinding.MaxConnections = oMaxConnections
LogConfig?.GetLogger()?.Debug("✔ NetTcpBinding configured | MaxConnections=[{0}] | SendTimeout=[{1}]",
oMaxConnections, oNetTcpBinding.SendTimeout)
End If
' ← NEU: Windows Auth Caching – reduziert DC-Anfragen bei parallelen Verbindungen
Dim oServiceCredentials = Config.Description.Behaviors.Find(Of ServiceCredentials)()
If oServiceCredentials Is Nothing Then
oServiceCredentials = New ServiceCredentials()
Config.Description.Behaviors.Add(oServiceCredentials)
End If
' Gruppenauflösung deaktivieren: weniger DC-Roundtrips pro Verbindung
oServiceCredentials.WindowsAuthentication.IncludeWindowsGroups = False
_ConfiguredEndpoint = oBaseAddress.ToString()
_ConfiguredMaxCalls = oThrottling.MaxConcurrentCalls
_ConfiguredMaxSessions = oThrottling.MaxConcurrentSessions
End Sub
Public Sub New()
Dim oOperationContext As OperationContext = OperationContext.Current
Dim oInstanceContext As InstanceContext = oOperationContext.InstanceContext
Dim oUsername = StripDomainFromUsername(oOperationContext.ServiceSecurityContext.WindowsIdentity.Name)
Dim oSessionId As String = If(oOperationContext.SessionId, $"no-session_{Guid.NewGuid().ToString("N").Substring(0, 8)}")
_Username = If(String.IsNullOrWhiteSpace(oUsername), "unknown", oUsername)
_Logger = LogConfig.GetLogger()
_IDBHelpers = New IDB.Helpers(LogConfig, MSSQL_IDB)
Dim oEndpointSnapshot = Interlocked.Exchange(Of String)(_ConfiguredEndpoint, String.Empty)
If Not String.IsNullOrEmpty(oEndpointSnapshot) Then
_Logger.Info("✔ EDMIService configured | Endpoint=[{0}] | MaxCalls=[{1}] | MaxSessions=[{2}] | CPUs=[{3}]",
oEndpointSnapshot, _ConfiguredMaxCalls, _ConfiguredMaxSessions, Environment.ProcessorCount)
End If
_ActiveSessions(oSessionId) = _Username
' ← Neu: Session-Start loggen
_Logger.Debug("Session opened | User=[{0}] | SessionId=[{1}] | ActiveSessions=[{2}]",
_Username, oSessionId, _ActiveSessions.Count)
AddHandler oInstanceContext.Closed, Sub()
Dim oRemoved As String = Nothing
_ActiveSessions.TryRemove(oSessionId, oRemoved)
_Logger.Debug("Session closed | User=[{0}] | SessionId=[{1}] | ActiveSessions=[{2}]",
_Username, oSessionId, _ActiveSessions.Count)
End Sub
AddHandler oInstanceContext.Faulted, Sub()
Dim oRemoved As String = Nothing
_ActiveSessions.TryRemove(oSessionId, oRemoved)
_Logger.Warn("⚠ Session faulted | User=[{0}] | SessionId=[{1}] | ActiveSessions=[{2}]",
_Username, oSessionId, _ActiveSessions.Count)
End Sub
End Sub
''' <summary>
''' Erstellt die User-Liste nur wenn nötig (Debug-Guard vorher prüfen).
''' </summary>
Private Shared Function BuildUserList() As String
Return String.Join(", ", _ActiveSessions.Values.Distinct().OrderBy(Function(u) u))
End Function
#Region "=== Base ==="
Public Function Heartbeat() As Boolean Implements IEDMIService.Heartbeat
Return True
End Function
Public Function GetClientConfig() As Base.GetClientConfig.GetClientConfigResponse Implements IEDMIService.GetClientConfig
_Logger.Debug("Start of Method [GetClientConfig]")
Dim oGetClientConfig As New Base.GetClientConfig.GetClientConfigMethod(LogConfig, MSSQL_IDB, MSSQL_ECM, GlobalState)
Return oGetClientConfig.Run()
End Function
''' <summary>
''' Returns a snapshot of all currently active sessions for monitoring purposes
''' </summary>
Public Function GetServiceStatus() As Messages.ServiceStatusResponse Implements IEDMIService.GetServiceStatus
_Logger.Debug("GetServiceStatus called by User=[{0}]", _Username)
' Snapshot nur einmal erstellen, nicht zweimal iterieren:
Dim oSnapshot = _ActiveSessions.ToArray()
Return New Messages.ServiceStatusResponse With {
.ActiveSessionCount = oSnapshot.Length,
.ActiveUsers = oSnapshot.Select(Function(p) p.Value).Distinct().OrderBy(Function(u) u).ToList(),
.ActiveSessions = oSnapshot.ToDictionary(Function(p) p.Key, Function(p) p.Value),
.Timestamp = DateTime.Now
}
End Function
#End Region
Public Function NewFile(Data As NewFile.NewFileRequest) As NewFile.NewFileResponse Implements IEDMIService.NewFile
_Logger.Debug("Start of Method [NewFile]")
Dim oNewFile As New NewFile.NewFileMethod(LogConfig, MSSQL_IDB, MSSQL_ECM, GlobalState)
Return oNewFile.Run(Data)
End Function
Public Function UpdateFile(Data As UpdateFile.UpdateFileRequest) As UpdateFile.UpdateFileResponse Implements IEDMIService.UpdateFile
_Logger.Debug("Start of Method [VersionFile]")
Dim oVersionFile As New UpdateFile.UpdateFileMethod(LogConfig, MSSQL_IDB, MSSQL_ECM, GlobalState)
Return oVersionFile.Run(Data)
End Function
Public Function SetAttributeValue(Data As SetAttributeValue.SetAttributeValueRequest) As SetAttributeValue.SetAttributeValueResponse Implements IEDMIService.SetAttributeValue
_Logger.Debug("Start of Method [SetAttributeValue]")
Dim oSetAttributeValue As New SetAttributeValue.SetAttributeValueMethod(LogConfig, MSSQL_IDB, MSSQL_ECM, GlobalState)
Return oSetAttributeValue.Run(Data)
End Function
Public Function GetAttributeValue(Data As GetAttributeValue.GetAttributeValueRequest) As GetAttributeValue.GetAttributeValueResponse Implements IEDMIService.GetAttributeValue
_Logger.Debug("Start of Method [GetAttributeValue]")
Dim oGetAttributeValue As New GetAttributeValue.GetAttributeValueMethod(LogConfig, MSSQL_IDB, MSSQL_ECM, GlobalState)
Return oGetAttributeValue.Run(Data)
End Function
Public Function GetFileObject(pData As GetFileObject.GetFileObjectRequest) As GetFileObject.GetFileObjectResponse Implements IEDMIService.GetFileObject
_Logger.Debug("Start of Method [GetFileObject]")
Dim oGetFileObject As New GetFileObject.GetFileObjectMethod(LogConfig, MSSQL_IDB, MSSQL_ECM, GlobalState)
Return oGetFileObject.Run(pData)
End Function
Public Function CheckInOutFile(pData As CheckInOutFile.CheckInOutFileRequest) As CheckInOutFile.CheckInOutFileResponse Implements IEDMIService.CheckInOutFile
_Logger.Debug("Start of Method [CheckInOutFile]")
Dim oCheckInOutFile As New CheckInOutFile.CheckInOutFileMethod(LogConfig, MSSQL_IDB, MSSQL_ECM, GlobalState)
Return oCheckInOutFile.Run(pData)
End Function
Public Function ImportFile(pData As ImportFile.ImportFileRequest) As ImportFile.ImportFileResponse Implements IEDMIService.ImportFile
_Logger.Debug("Start of Method [IDB.ImportFile]")
Dim oImportFile As New ImportFile.ImportFileMethod(LogConfig, MSSQL_IDB, MSSQL_ECM, GlobalState)
Return oImportFile.Run(pData)
End Function
Public Function Globix_ImportFile(pData As GlobalIndexer.ImportFile.Globix_ImportFileRequest) As GlobalIndexer.ImportFile.Globix_ImportFileResponse Implements IEDMIService.Globix_ImportFile
_Logger.Debug("Start of Method [Globix.ImportFile]")
Dim oImportFile As New GlobalIndexer.ImportFile.ImportFileMethod(LogConfig, MSSQL_IDB, MSSQL_ECM, GlobalState)
Return oImportFile.Run(pData)
End Function
#Region "=== Database ==="
' ── CachedTables-Snapshot: wird vom JobListener nach dem DatatableJob gesetzt.
' GetCachedTables() liest nur diesen Snapshot – ruft GlobalState nie direkt auf
' und blockiert daher nicht, auch wenn der DatatableJob den TableStore sperrt.
Private Class CachedTablesState
Public ReadOnly Tables As String()
Public ReadOnly Timestamp As DateTime
Public Sub New(pTables As String(), pTimestamp As DateTime)
Tables = pTables
Timestamp = pTimestamp
End Sub
End Class
Private Shared _CachedTablesState As CachedTablesState = New CachedTablesState(Array.Empty(Of String)(), DateTime.MinValue)
''' <summary>
''' Wird vom JobListener nach erfolgreichem DatatableJob aufgerufen.
''' Setzt den Snapshot atomar – kein Lock nötig, da Array-Referenzzuweisung auf CLR atomar ist.
''' </summary>
Public Shared Sub PublishCachedTablesSnapshot(pTableNames As IEnumerable(Of String))
Dim oTables = pTableNames.Select(Function(t) t.ToUpper()).ToArray()
Dim oNewState = New CachedTablesState(oTables, DateTime.Now)
' Atomar: Timestamp und Array immer gemeinsam sichtbar
Interlocked.Exchange(_CachedTablesState, oNewState)
LogConfig?.GetLogger()?.Debug("PublishCachedTablesSnapshot: [{0}] tables published at [{1}]",
oTables.Length, oNewState.Timestamp.ToString("HH:mm:ss"))
End Sub
Public Function GetCachedTables() As List(Of String) Implements IEDMIService.GetCachedTables
Dim oState = _CachedTablesState ' atomarer Read – eine Referenz
_Logger.Debug("GetCachedTables: returning snapshot | Count=[{0}] | SnapshotAge=[{1}s]",
oState.Tables.Length,
CInt((DateTime.Now - oState.Timestamp).TotalSeconds))
Return oState.Tables.ToList()
End Function
Public Function ReturnDatatableFromCache(Name As String, FilterExpression As String, SortByColumn As String) As TableResult Implements IEDMIService.ReturnDatatableFromCache
Dim oReturnDatatableFromCache As New GetDatatableFromCache.GetDatatableFromCacheMethod(LogConfig, MSSQL_IDB, MSSQL_ECM, GlobalState)
Dim oResult = oReturnDatatableFromCache.Run(New GetDatatableFromCache.GetDatatableFromCacheRequest With {
.DataTable = Name,
.FilterExpression = FilterExpression,
.SortByColumn = SortByColumn
})
If oResult.OK Then
Return New TableResult(oResult.Table)
Else
Return New TableResult(oResult.ErrorMessage)
End If
End Function
Public Function ReturnDatatable(pData As GetDatatable.GetDatatableRequest) As GetDatatable.GetDatatableResponse Implements IEDMIService.ReturnDatatable
Dim oWatch = Stopwatch.StartNew()
Try
_Logger.Debug("ReturnDatatable | User=[{0}] | DB=[{1}] | SQL=[{2}]",
_Username, pData.NamedDatabase, pData.SqlCommand)
Dim oGetDatatable As New GetDatatable.GetDatatableMethod(LogConfig, MSSQL_IDB, MSSQL_ECM, GlobalState)
Dim oResult = oGetDatatable.Run(pData)
_Logger.Debug("ReturnDatatable completed | User=[{0}] | Rows=[{1}] | Duration=[{2}ms]",
_Username, If(oResult?.Table?.Rows.Count, 0), oWatch.ElapsedMilliseconds)
Return oResult
Catch ex As Exception
_Logger.Error("ReturnDatatable failed | User=[{0}] | Duration=[{1}ms] | Error=[{2}]",
_Username, oWatch.ElapsedMilliseconds, ex.Message)
Throw
End Try
End Function
Public Function ReturnScalarValue(pData As GetScalarValue.GetScalarValueRequest) As GetScalarValue.GetScalarValueResponse Implements IEDMIService.ReturnScalarValue
Dim oWatch = Stopwatch.StartNew()
Try
_Logger.Debug("ReturnScalarValue | User=[{0}] | DB=[{1}] | SQL=[{2}]",
_Username, pData.NamedDatabase, pData.SqlCommand)
Dim oGetScalarValue As New GetScalarValue.GetScalarValueMethod(LogConfig, MSSQL_IDB, MSSQL_ECM, GlobalState)
Dim oResult = oGetScalarValue.Run(pData)
_Logger.Debug("ReturnScalarValue completed | User=[{0}] | Duration=[{1}ms]",
_Username, oWatch.ElapsedMilliseconds)
Return oResult
Catch ex As Exception
_Logger.Error("ReturnScalarValue failed | User=[{0}] | Duration=[{1}ms] | Error=[{2}]",
_Username, oWatch.ElapsedMilliseconds, ex.Message)
Throw
End Try
End Function
Public Function ExecuteNonQuery(pData As ExecuteNonQuery.ExecuteNonQueryRequest) As ExecuteNonQuery.ExecuteNonQueryResponse Implements IEDMIService.ExecuteNonQuery
Dim oWatch = Stopwatch.StartNew()
Try
_Logger.Debug("ExecuteNonQuery | User=[{0}] | DB=[{1}] | SQL=[{2}]",
_Username, pData.NamedDatabase, pData.SqlCommand)
Dim oExecuteNonQuery As New ExecuteNonQuery.ExecuteNonQueryMethod(LogConfig, MSSQL_IDB, MSSQL_ECM, GlobalState)
Dim oResult = oExecuteNonQuery.Run(pData)
_Logger.Debug("ExecuteNonQuery completed | User=[{0}] | Duration=[{1}ms]",
_Username, oWatch.ElapsedMilliseconds)
Return oResult
Catch ex As Exception
_Logger.Error("ExecuteNonQuery failed | User=[{0}] | Duration=[{1}ms] | Error=[{2}]",
_Username, oWatch.ElapsedMilliseconds, ex.Message)
Throw
End Try
End Function
#End Region
#Region "=== Database (MSSQL IDB) ==="
Public Function ReturnDatatable_MSSQL_IDB(SQL As String) As TableResult Implements IEDMIService.ReturnDatatable_MSSQL_IDB
Try
_Logger.Debug($"ReturnDatatable_MSSQL_IDB, SQL: {SQL}")
Dim oResult As DataTable = MSSQL_IDB.GetDatatable(SQL)
oResult.TableName = "DD_RESULT"
Return New TableResult(oResult)
Catch ex As FaultException
_Logger.Error(ex)
Throw
Catch ex As Exception
_Logger.Error(ex)
Throw GetFault(ex)
End Try
End Function
Public Function ReturnScalar_MSSQL_IDB(SQL As String) As ScalarResult Implements IEDMIService.ReturnScalar_MSSQL_IDB
Try
_Logger.Info($"ReturnScalar_MSSQL_IDB, SQL: {SQL}")
Dim oResult As Object = MSSQL_IDB.GetScalarValue(SQL)
Return New ScalarResult(oResult)
Catch ex As FaultException
_Logger.Error(ex)
Throw
Catch ex As Exception
_Logger.Error(ex)
Throw GetFault(ex)
End Try
End Function
Public Function ExecuteNonQuery_MSSQL_IDB(SQL As String) As NonQueryResult Implements IEDMIService.ExecuteNonQuery_MSSQL_IDB
Try
_Logger.Info($"ExecuteNonQuery_MSSQL_IDB, SQL: {SQL}")
Dim oResult As Boolean = MSSQL_IDB.ExecuteNonQuery(SQL)
Return New NonQueryResult()
Catch ex As FaultException
_Logger.Error(ex)
Throw
Catch ex As Exception
_Logger.Error(ex)
Throw GetFault(ex)
End Try
End Function
#End Region
#Region "=== Database (MSSQL ECM) ==="
Public Function ReturnDatatable_MSSQL_ECM(SQL As String) As TableResult Implements IEDMIService.ReturnDatatable_MSSQL_ECM
Try
_Logger.Info($"ReturnDatatable_MSSQL_ECM, SQL: {SQL}")
Dim oResult As DataTable = MSSQL_ECM.GetDatatable(SQL)
oResult.TableName = "DD_RESULT"
Return New TableResult(oResult)
Catch ex As FaultException
_Logger.Error(ex)
Throw
Catch ex As Exception
_Logger.Error(ex)
Throw GetFault(ex)
End Try
End Function
Public Function ReturnScalar_MSSQL_ECM(SQL As String) As ScalarResult Implements IEDMIService.ReturnScalar_MSSQL_ECM
Try
_Logger.Info($"ReturnScalar_MSSQL_ECM, SQL: {SQL}")
Dim oResult As Object = MSSQL_ECM.GetScalarValue(SQL)
Return New ScalarResult(oResult)
Catch ex As FaultException
_Logger.Error(ex)
Throw
Catch ex As Exception
_Logger.Error(ex)
Throw GetFault(ex)
End Try
End Function
Public Function ExecuteNonQuery_MSSQL_ECM(SQL As String) As NonQueryResult Implements IEDMIService.ExecuteNonQuery_MSSQL_ECM
Try
_Logger.Info($"ExecuteNonQuery_MSSQL_ECM, SQL: {SQL}")
Dim oResult As Boolean = MSSQL_ECM.ExecuteNonQuery(SQL)
Return New NonQueryResult()
Catch ex As FaultException
_Logger.Error(ex)
Throw
Catch ex As Exception
_Logger.Error(ex)
Throw GetFault(ex)
End Try
End Function
#End Region
#Region "=== Database (Firebird) ==="
Public Function ReturnDatatable_Firebird(SQL As String) As TableResult Implements IEDMIService.ReturnDatatable_Firebird
Try
_Logger.Info($"ReturnDatatable, SQL: {SQL}")
Dim oResult As DataTable = Firebird.GetDatatable(SQL)
oResult.TableName = "DD_RESULT"
Return New TableResult(oResult)
Catch ex As FaultException
_Logger.Error(ex)
Throw
Catch ex As Exception
_Logger.Error(ex)
Throw GetFault(ex)
End Try
End Function
Public Function ReturnScalar_Firebird(SQL As String) As ScalarResult Implements IEDMIService.ReturnScalar_Firebird
Try
_Logger.Info($"ReturnScalar, SQL: {SQL}")
Dim oResult As Object = Firebird.GetScalarValue(SQL)
Return New ScalarResult(oResult)
Catch ex As FaultException
_Logger.Error(ex)
Throw
Catch ex As Exception
_Logger.Error(ex)
Throw GetFault(ex)
End Try
End Function
Public Function ExecuteNonQuery_Firebird(SQL As String) As NonQueryResult Implements IEDMIService.ExecuteNonQuery_Firebird
Try
_Logger.Info($"ExecuteNonQuery, SQL: {SQL}")
Dim oResult As Boolean = Firebird.ExecuteNonQuery(SQL)
Return New NonQueryResult()
Catch ex As FaultException
_Logger.Error(ex)
Throw
Catch ex As Exception
_Logger.Error(ex)
Throw GetFault(ex)
End Try
End Function
#End Region
#Region "=== Document ==="
''' <summary>
''' Imports a file according to ObjectStoreId
''' </summary>
''' <returns></returns>
'Public Function ImportFile(Data As DocumentImportRequest) As DocumentImportResponse Implements IEDMIService.ImportFile
' Dim oObjectStore = GlobalState.ObjectStores.First()
' Dim EDMIPath = New EDMI.File.Path(LogConfig, oObjectStore.Path)
' ' TODO:
' ' - Get Object Store -> Object Catalog -> Catalog Path
' ' - If IS_ARCHIVE = True And RetentionDays <> Nothing:
' ' DoArchive!
' ' - Refactor EDMIPath to get ObjectStore at service start up
' ' and return ObjectStore Path from ObjectStoreId + RelativePath
' ' VWIDB_OBJECTSTORE
' Dim oRelativePath As String = EDMIPath.GetRelativePath(Data.DocumentType, Data.FileName)
' Dim oAbsolutePath As String = EDMIPath.GetFullPath(Data.DocumentType, Data.FileName)
' Dim oDirectoryPath = EDMIPath.GetFullPath(Data.DocumentType)
' Try
' Directory.CreateDirectory(oDirectoryPath)
' Dim oVersionedFileName As String = Filesystem.GetVersionedFilename(oAbsolutePath)
' _Logger.Info("ImportFile: Saving file [{0}] to path [{1}]", Data.FileName, oVersionedFileName)
' Using oStream = New FileStream(oVersionedFileName, FileMode.CreateNew)
' oStream.Write(Data.Contents, 0, Data.Contents.Length)
' oStream.Flush(True)
' oStream.Close()
' End Using
' ' insert into db
' Dim oCommand As New SqlCommand("PRIDB_NEW_DOCUMENT")
' oCommand.Parameters.AddWithValue("@OBJ_ST_ID", 1)
' oCommand.Parameters.AddWithValue("@REL_PATH", oRelativePath)
' oCommand.Parameters.AddWithValue("@WHO", _Username)
' oCommand.Parameters.AddWithValue("@REF_DOCID", 0)
' oCommand.Parameters.Add(New SqlParameter("@IDB_OBJ_ID", SqlDbType.BigInt))
' Dim oObjectId = MSSQL_IDB.GetScalarValue(oCommand, "@IDB_OBJ_ID")
' Return New DocumentImportResponse() With {.ObjectId = oObjectId}
' Catch ex As FaultException
' _Logger.Error(ex)
' Throw ex
' Catch ex As Exception
' _Logger.Error(ex)
' Throw GetFault(ex)
' End Try
'End Function
Public Function GetFileByObjectId(Data As DocumentStreamRequest) As DocumentStreamResponse Implements IEDMIService.GetFileByObjectId
Try
Dim oFullPath = GetFullPathForObjectId(Data.ObjectId)
If oFullPath = String.Empty Then
Dim oDetails As New ObjectDoesNotExistFault(Data.ObjectId)
_Logger.Warn("⚠ GetFileByObjectId: " & oDetails.ErrorMessage)
Throw New FaultException(Of ObjectDoesNotExistFault)(oDetails, oDetails.ErrorMessage)
End If
_Logger.Debug("GetFileByObjectId: Loading file [{0}]", oFullPath)
Dim oFileInfo As New FileInfo(oFullPath)
If Not oFileInfo.Exists Then
Dim oDetails As New ObjectDoesNotExistFault(Data.ObjectId)
_Logger.Warn("⚠ GetFileByObjectId: " & oDetails.ErrorMessage)
Throw New FaultException(Of ObjectDoesNotExistFault)(oDetails, oDetails.ErrorMessage)
End If
Dim oDestination As New MemoryStream()
Using oSource As FileStream = IO.File.OpenRead(oFullPath)
oSource.CopyTo(oDestination)
End Using
oDestination.Seek(0, SeekOrigin.Begin)
Dim oMessage As New DocumentStreamResponse() With {
.FileName = oFileInfo.Name,
.FileContents = oDestination
}
Return oMessage
Catch ex As FaultException
_Logger.Error(ex)
Throw
Catch ex As Exception
_Logger.Error(ex)
Throw GetFault(ex)
End Try
End Function
Public Function GetFileInfoByObjectId(Data As DocumentInfoRequest) As DocumentInfoResponse Implements IEDMIService.GetFileInfoByObjectId
Try
_Logger.Debug("GetFileInfoByObjectId...")
Dim oAccessRight = GetAccessRightForObjectId(Data.UserId, Data.ObjectId)
Dim oFullPath = GetFullPathForObjectId(Data.ObjectId)
If oFullPath = String.Empty Then
Dim oFault As New ObjectDoesNotExistFault(Data.ObjectId)
Throw New FaultException(Of ObjectDoesNotExistFault)(oFault, oFault.ErrorMessage)
End If
Dim oResult = New DocumentInfoResponse With {
.FileRight = oAccessRight,
.FullPath = oFullPath
}
_Logger.Debug("End GetFileInfoByObjectId!") ' ← jetzt erreichbar
Return oResult
Catch ex As FaultException
_Logger.Warn("⚠ GetFileInfoByObjectId: " & ex.Message)
_Logger.Error(ex)
Throw
Catch ex As Exception
_Logger.Warn("⚠ GetFileInfoByObjectId: " & ex.Message)
_Logger.Error(ex)
Throw GetFault(ex)
End Try
End Function
Public Function ListFilesForUser() As DocumentListResponse Implements IEDMIService.ListFilesForUser
Try
Dim oDatatable = GetFileList()
Return New DocumentListResponse() With {
.Datatable = oDatatable
}
Catch ex As FaultException
_Logger.Error(ex)
Throw
Catch ex As Exception
_Logger.Error(ex)
Throw GetFault(ex)
End Try
End Function
Public Function GetFileList() As DataTable
Try
If Not GlobalState.TableStore.Tables.Contains(TBIDB_DOC_INFO) Then
_Logger.Warn("⚠ GetFileList: Document info table does not exist!")
Return Nothing
End If
Dim oTable As DataTable = GlobalState.TableStore.Tables.Item(TBIDB_DOC_INFO)
Return oTable
Catch ex As Exception
_Logger.Warn("⚠ GetFileList: Unexpected Error while getting file list.")
_Logger.Error(ex)
Return Nothing
End Try
End Function
Public Function TestObjectIdExists(Data As TestObjectIdExistsRequest) As TestObjectIdExistsResponse Implements IEDMIService.TestObjectIdExists
Try
Dim oDeleted As Boolean = False
Dim oActive As Boolean = False
If _IDBHelpers.TestObjectIdExists(Data.ObjectId, oDeleted, oActive) = False Then
Return New TestObjectIdExistsResponse(False)
End If
Return New TestObjectIdExistsResponse(True) With {
.Deleted = oDeleted,
.Inactive = Not oActive
}
Catch ex As Exception
_Logger.Error(ex)
Return New TestObjectIdExistsResponse(False)
End Try
End Function
#End Region
#Region "=== Private ==="
Private Function GetFault(Exception As Exception) As FaultException
Dim oMessage = If(Exception?.Message, "Unknown error")
_Logger.Error("Wrapping exception as FaultException | Error=[{0}]", oMessage)
Dim oFault As New UnexpectedErrorFault(Exception)
Dim oReason = If(Exception?.InnerException?.Message, oMessage)
Return New FaultException(Of UnexpectedErrorFault)(oFault, New FaultReason(oReason))
End Function
Private Function GetFault(ErrorMessage As String) As FaultException
_Logger.Error("Wrapping error as FaultException | Error=[{0}]", ErrorMessage)
Dim oFault As New UnexpectedErrorFault(New ApplicationException(ErrorMessage))
Return New FaultException(Of UnexpectedErrorFault)(oFault, New FaultReason(ErrorMessage))
End Function
Private Function GetFullPathForObjectId(ObjectId As Long) As String
Try
If Not GlobalState.TableStore.Tables.Contains(TBIDB_DOC_INFO) Then
_Logger.Warn("⚠ GetFullPathForObjectId: Document info table does not exist!")
Return String.Empty
End If
Dim oTable As DataTable = GlobalState.TableStore.Tables.Item(TBIDB_DOC_INFO)
Dim oRows As List(Of DataRow) = oTable.Select($"IDB_OBJ_ID = {ObjectId}").ToList()
Dim oFullPath As String
If oRows.Count = 0 Then
_Logger.Warn("⚠ GetFullPathForObjectId: Full path does not exist for object [{0}]", ObjectId)
oFullPath = String.Empty
Else
Dim oRow As DataRow = oRows.First()
oFullPath = oRow.Item("FULL_PATH")
End If
Return oFullPath
Catch ex As Exception
_Logger.Warn("⚠ GetFullPathForObjectId: Unexpected Error while getting full path for object [{0}].", ObjectId)
_Logger.Error(ex)
Return String.Empty
End Try
End Function
Private Function GetAccessRightForObjectId(UserId As Long, ObjectId As Long) As AccessRight
Try
If Not GlobalState.TableStore.Tables.Contains(TBIDB_ACCESSRIGHT) Then
_Logger.Warn("⚠ GetAccessRightForObjectId: Access right table does not exist!")
Return AccessRight.VIEW_ONLY
End If
_Logger.Debug("Getting AccessRights for ObjectId [{0}]", ObjectId)
Dim oTable As DataTable = GlobalState.TableStore.Tables.Item(TBIDB_ACCESSRIGHT)
Dim oRows As List(Of DataRow) = oTable.Select($"IDB_OBJ_ID = {ObjectId} AND USR_ID = {UserId}").ToList()
Dim oRight As AccessRight
_Logger.Debug("Successfully got AccessRights for ObjectId [{0}]", ObjectId)
_Logger.Debug("Parsing AccessRights for ObjectId [{0}]", ObjectId)
If oRows.Count = 0 Then
_Logger.Warn("⚠ GetAccessRightForObjectId: Access right assignment does not exist for user [{0}] on object [{1}]", UserId, ObjectId)
Return AccessRight.VIEW_ONLY
Else
If oRows.Count > 1 Then
_Logger.Warn("⚠ GetAccessRightForObjectId: More than one access right assignment found for user [{0}] on object [{1}]", UserId, ObjectId)
End If
Dim oRow As DataRow = oRows.First()
Dim oRightAsInt = oRow.Item("ACCESSRIGHT")
Select Case oRightAsInt
Case 1
oRight = AccessRight.VIEW_ONLY
Case 2
oRight = AccessRight.VIEW_EXPORT
Case 4
oRight = AccessRight.FULL
Case Else
_Logger.Warn("⚠ GetAccessRightForObjectId: Unknown access right [{0}] for user [{1}] on object [{2}]", oRightAsInt, UserId, ObjectId)
oRight = AccessRight.VIEW_ONLY
End Select
End If
_Logger.Debug("Successfully parsed AccessRights for ObjectId [{0}]", ObjectId)
_Logger.Debug("AccessRight for ObjectId [{0}] is [{1}]", ObjectId, oRight.ToString)
Return oRight
Catch ex As Exception
_Logger.Warn("⚠ GetAccessRightForObjectId: Unexpected Error while getting access right for object [{0}].", ObjectId)
_Logger.Error(ex)
Return AccessRight.VIEW_ONLY
End Try
End Function
Private Function StripDomainFromUsername(UserName As String)
If UserName.Contains("\") Then
Return UserName.Split("\")(1)
ElseIf UserName.Contains("@") Then
Return UserName.Split("@")(0)
Else
Return UserName
End If
End Function
#End Region
End Class