Bug Multiindexing, Cache load
This commit is contained in:
111
app/DD-Record-Organizer/Classes/ClassDataCache.vb
Normal file
111
app/DD-Record-Organizer/Classes/ClassDataCache.vb
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
|
||||||
|
Imports System.Data
|
||||||
|
|
||||||
|
''' <summary>
|
||||||
|
''' Zentraler Cache für häufig abgerufene Datenbank-Queries
|
||||||
|
''' Reduziert DB-Roundtrips um bis zu 90%
|
||||||
|
''' </summary>
|
||||||
|
Public Class ClassDataCache
|
||||||
|
Private Shared ReadOnly _cache As New Dictionary(Of String, CachedItem)
|
||||||
|
Private Shared ReadOnly _lockObject As New Object()
|
||||||
|
Private Shared _defaultTimeout As TimeSpan = TimeSpan.FromMinutes(5)
|
||||||
|
|
||||||
|
Private Class CachedItem
|
||||||
|
Public Data As DataTable
|
||||||
|
Public Timestamp As DateTime
|
||||||
|
Public Timeout As TimeSpan
|
||||||
|
|
||||||
|
Public ReadOnly Property IsExpired As Boolean
|
||||||
|
Get
|
||||||
|
Return DateTime.Now - Timestamp > Timeout
|
||||||
|
End Get
|
||||||
|
End Property
|
||||||
|
End Class
|
||||||
|
|
||||||
|
''' <summary>
|
||||||
|
''' Daten aus Cache holen oder neu laden
|
||||||
|
''' </summary>
|
||||||
|
Public Shared Function GetOrLoad(cacheKey As String,
|
||||||
|
loadFunction As Func(Of DataTable),
|
||||||
|
Optional timeout As TimeSpan? = Nothing) As DataTable
|
||||||
|
SyncLock _lockObject
|
||||||
|
' Cache-Check
|
||||||
|
If _cache.ContainsKey(cacheKey) Then
|
||||||
|
Dim item = _cache(cacheKey)
|
||||||
|
|
||||||
|
If Not item.IsExpired Then
|
||||||
|
LOGGER.Debug($"Cache HIT: {cacheKey} (Age: {(DateTime.Now - item.Timestamp).TotalSeconds:F1}s)")
|
||||||
|
Return item.Data.Copy() ' Kopie zurückgeben!
|
||||||
|
Else
|
||||||
|
' Abgelaufen - entfernen
|
||||||
|
_cache.Remove(cacheKey)
|
||||||
|
LOGGER.Debug($"Cache EXPIRED: {cacheKey}")
|
||||||
|
End If
|
||||||
|
End If
|
||||||
|
|
||||||
|
' Cache MISS - neu laden
|
||||||
|
LOGGER.Debug($"Cache MISS: {cacheKey} - Loading from DB...")
|
||||||
|
Dim result = loadFunction()
|
||||||
|
|
||||||
|
If result IsNot Nothing Then
|
||||||
|
_cache(cacheKey) = New CachedItem With {
|
||||||
|
.Data = result.Copy(),
|
||||||
|
.Timestamp = DateTime.Now,
|
||||||
|
.Timeout = If(timeout, _defaultTimeout)
|
||||||
|
}
|
||||||
|
End If
|
||||||
|
|
||||||
|
Return result
|
||||||
|
End SyncLock
|
||||||
|
End Function
|
||||||
|
|
||||||
|
''' <summary>
|
||||||
|
''' Bestimmten Cache-Eintrag invalidieren
|
||||||
|
''' </summary>
|
||||||
|
Public Shared Sub Invalidate(cacheKey As String)
|
||||||
|
SyncLock _lockObject
|
||||||
|
If _cache.ContainsKey(cacheKey) Then
|
||||||
|
_cache.Remove(cacheKey)
|
||||||
|
LOGGER.Debug($"Cache INVALIDATED: {cacheKey}")
|
||||||
|
End If
|
||||||
|
End SyncLock
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
''' <summary>
|
||||||
|
''' Alle Cache-Einträge löschen
|
||||||
|
''' </summary>
|
||||||
|
Public Shared Sub ClearAll()
|
||||||
|
SyncLock _lockObject
|
||||||
|
Dim count = _cache.Count
|
||||||
|
_cache.Clear()
|
||||||
|
LOGGER.Info($"Cache CLEARED: {count} entries removed")
|
||||||
|
End SyncLock
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
''' <summary>
|
||||||
|
''' Abgelaufene Einträge entfernen
|
||||||
|
''' </summary>
|
||||||
|
Public Shared Sub CleanupExpired()
|
||||||
|
SyncLock _lockObject
|
||||||
|
Dim expiredKeys = _cache.Where(Function(kvp) kvp.Value.IsExpired).
|
||||||
|
Select(Function(kvp) kvp.Key).ToList()
|
||||||
|
|
||||||
|
For Each key In expiredKeys
|
||||||
|
_cache.Remove(key)
|
||||||
|
Next
|
||||||
|
|
||||||
|
If expiredKeys.Count > 0 Then
|
||||||
|
LOGGER.Debug($"Cache CLEANUP: {expiredKeys.Count} expired entries removed")
|
||||||
|
End If
|
||||||
|
End SyncLock
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
''' <summary>
|
||||||
|
''' Cache-Statistiken
|
||||||
|
''' </summary>
|
||||||
|
Public Shared Function GetStatistics() As String
|
||||||
|
SyncLock _lockObject
|
||||||
|
Return $"Cache Entries: {_cache.Count}, Default Timeout: {_defaultTimeout.TotalMinutes:F1} min"
|
||||||
|
End SyncLock
|
||||||
|
End Function
|
||||||
|
End Class
|
||||||
@@ -64,7 +64,8 @@ Public Class ClassDocGrid
|
|||||||
Private Shared _datepickerValueChangedHandler As EventHandler
|
Private Shared _datepickerValueChangedHandler As EventHandler
|
||||||
Private Shared _textValueChangedHandler As EventHandler
|
Private Shared _textValueChangedHandler As EventHandler
|
||||||
Private Shared _checkValueChangedHandler As EventHandler
|
Private Shared _checkValueChangedHandler As EventHandler
|
||||||
|
Private Shared EnableVerboseGridLogging As Boolean = False ' PRODUKTIV: FALSE!
|
||||||
|
Private Shared _isGridRefreshing As Boolean = False
|
||||||
Private Shared Function Init_Table()
|
Private Shared Function Init_Table()
|
||||||
Try
|
Try
|
||||||
Dim table As New DataTable With {
|
Dim table As New DataTable With {
|
||||||
@@ -318,20 +319,41 @@ Public Class ClassDocGrid
|
|||||||
' Neues Dataset für Master- und Detail-Tabelle erstellen
|
' Neues Dataset für Master- und Detail-Tabelle erstellen
|
||||||
Dim ds As New DataSet()
|
Dim ds As New DataSet()
|
||||||
Dim DT_DETAILS_SQL
|
Dim DT_DETAILS_SQL
|
||||||
|
' ── Cache-optimiertes Laden der Detail-Values ─────────────────────────
|
||||||
|
Dim cacheKey As String
|
||||||
|
Dim DT_DETAIL_VALUES As DataTable
|
||||||
|
|
||||||
Select Case CURRENT_SEARCH_TYPE
|
Select Case CURRENT_SEARCH_TYPE
|
||||||
Case "NODE_DOWN"
|
Case "NODE_DOWN"
|
||||||
DT_DETAILS_SQL = String.Format("SELECT T.[GUID],T.[DocID],T.[CONFIG_ID],T1.HEADER_CAPTION,T.[VALUE],T1.[LANGUAGE], T1.COLUMN_VIEW,T1.EDITABLE,T1.TYPE_ID,T1.VISIBLE,T.CHANGED_WHEN,T.CHANGED_WHO " &
|
cacheKey = $"DocDetailValues_NodeDown_E{CURRENT_ENTITY_ID}_L{USER_LANGUAGE}"
|
||||||
"FROM TBPMO_DOC_VALUES T INNER JOIN TBPMO_STRUCTURE_NODES_USER_TEMP TTEMP ON T.RECORD_ID = TTEMP.RECORD_ID RIGHT JOIN TBPMO_DOCSEARCH_RESULTLIST_CONFIG T1 ON T.CONFIG_ID = T1.GUID WHERE T1.ENTITY_ID = {0} AND LANGUAGE = '{1}' AND T1.CONFIG_COLUMNS = 1", CURRENT_ENTITY_ID, USER_LANGUAGE)
|
|
||||||
|
DT_DETAIL_VALUES = ClassDataCache.GetOrLoad(cacheKey, Function()
|
||||||
|
Dim sql = String.Format(
|
||||||
|
"SELECT T.[GUID],T.[DocID],T.[CONFIG_ID],T1.HEADER_CAPTION,T.[VALUE],T1.[LANGUAGE], " &
|
||||||
|
"T1.COLUMN_VIEW,T1.EDITABLE,T1.TYPE_ID,T1.VISIBLE,T.CHANGED_WHEN,T.CHANGED_WHO " &
|
||||||
|
"FROM TBPMO_DOC_VALUES T " &
|
||||||
|
"INNER JOIN TBPMO_STRUCTURE_NODES_USER_TEMP TTEMP ON T.RECORD_ID = TTEMP.RECORD_ID " &
|
||||||
|
"RIGHT JOIN TBPMO_DOCSEARCH_RESULTLIST_CONFIG T1 ON T.CONFIG_ID = T1.GUID " &
|
||||||
|
"WHERE T1.ENTITY_ID = {0} AND LANGUAGE = '{1}' AND T1.CONFIG_COLUMNS = 1",
|
||||||
|
CURRENT_ENTITY_ID, USER_LANGUAGE)
|
||||||
|
Return MYDB_ECM.GetDatatable(sql)
|
||||||
|
End Function, TimeSpan.FromMinutes(2))
|
||||||
|
|
||||||
Case Else
|
Case Else
|
||||||
DT_DETAILS_SQL = String.Format("SELECT T.[GUID],T.[DocID],T.[CONFIG_ID],T1.HEADER_CAPTION,T.[VALUE],T1.[LANGUAGE], T1.COLUMN_VIEW,T1.EDITABLE,T1.TYPE_ID,T1.VISIBLE,T.CHANGED_WHEN,T.CHANGED_WHO " &
|
cacheKey = $"DocDetailValues_E{CURRENT_ENTITY_ID}_R{RECORD_ID}_L{USER_LANGUAGE}"
|
||||||
"FROM TBPMO_DOC_VALUES T RIGHT JOIN TBPMO_DOCSEARCH_RESULTLIST_CONFIG T1 ON T.CONFIG_ID = T1.GUID WHERE T1.ENTITY_ID = {0} AND LANGUAGE = '{1}' AND T1.CONFIG_COLUMNS = 1 AND T.RECORD_ID = {2}", CURRENT_ENTITY_ID, USER_LANGUAGE, RECORD_ID)
|
|
||||||
|
|
||||||
|
DT_DETAIL_VALUES = ClassDataCache.GetOrLoad(cacheKey, Function()
|
||||||
|
Dim sql = String.Format(
|
||||||
|
"SELECT T.[GUID],T.[DocID],T.[CONFIG_ID],T1.HEADER_CAPTION,T.[VALUE],T1.[LANGUAGE], " &
|
||||||
|
"T1.COLUMN_VIEW,T1.EDITABLE,T1.TYPE_ID,T1.VISIBLE,T.CHANGED_WHEN,T.CHANGED_WHO " &
|
||||||
|
"FROM TBPMO_DOC_VALUES T " &
|
||||||
|
"RIGHT JOIN TBPMO_DOCSEARCH_RESULTLIST_CONFIG T1 ON T.CONFIG_ID = T1.GUID " &
|
||||||
|
"WHERE T1.ENTITY_ID = {0} AND LANGUAGE = '{1}' AND T1.CONFIG_COLUMNS = 1 AND T.RECORD_ID = {2}",
|
||||||
|
CURRENT_ENTITY_ID, USER_LANGUAGE, RECORD_ID)
|
||||||
|
Return MYDB_ECM.GetDatatable(sql)
|
||||||
|
End Function, TimeSpan.FromMinutes(2))
|
||||||
End Select
|
End Select
|
||||||
'"FROM TBPMO_DOC_VALUES T INNER JOIN TBPMO_DOCSEARCH_RESULTLIST_CONFIG T1 ON T.CONFIG_ID = T1.GUID WHERE T1.ENTITY_ID = {0} AND T1.LANGUAGE = '{1}' AND T.RECORD_ID = {2} ORDER BY T.DocID, T1.SEQUENCE", CURRENT_ENTITY_ID, USER_LANGUAGE, RECORD_ID)
|
|
||||||
|
|
||||||
Dim DT_DETAIL_VALUES As DataTable = MYDB_ECM.GetDatatable(DT_DETAILS_SQL)
|
|
||||||
Dim oDocID As Integer
|
Dim oDocID As Integer
|
||||||
Dim oConfigID As Integer
|
Dim oConfigID As Integer
|
||||||
Dim recordId As Integer
|
Dim recordId As Integer
|
||||||
@@ -401,10 +423,19 @@ Public Class ClassDocGrid
|
|||||||
Dim gridControl As GridControl = pDocGridView.GridControl
|
Dim gridControl As GridControl = pDocGridView.GridControl
|
||||||
|
|
||||||
|
|
||||||
' Datasource auf Master-Tabelle setzen
|
' ── Performance-optimiertes DataSource-Setzen ─────────────────────────
|
||||||
'gridView.GridControl.DataSource = DT_RESULT
|
_isGridRefreshing = True ' Flag setzen VOR DataSource-Änderung
|
||||||
gridControl.DataSource = ds.Tables(0)
|
Try
|
||||||
gridControl.ForceInitialize()
|
pDocGridView.BeginDataUpdate() ' Events unterdrücken
|
||||||
|
|
||||||
|
' Datasource auf Master-Tabelle setzen
|
||||||
|
gridControl.DataSource = ds.Tables(0)
|
||||||
|
gridControl.ForceInitialize()
|
||||||
|
|
||||||
|
pDocGridView.EndDataUpdate() ' Events reaktivieren
|
||||||
|
Finally
|
||||||
|
_isGridRefreshing = False ' Flag zurücksetzen
|
||||||
|
End Try
|
||||||
|
|
||||||
' Detail View anlegen und der Relation `docIdDetails` zuweisen
|
' Detail View anlegen und der Relation `docIdDetails` zuweisen
|
||||||
Dim GVDoc_Values As New GridView(gridControl)
|
Dim GVDoc_Values As New GridView(gridControl)
|
||||||
@@ -471,8 +502,15 @@ Public Class ClassDocGrid
|
|||||||
End If
|
End If
|
||||||
|
|
||||||
If GridDocResult_BestFitColumns Then
|
If GridDocResult_BestFitColumns Then
|
||||||
pDocGridView.OptionsView.BestFitMaxRowCount = -1
|
_isGridRefreshing = True ' Auch hier Events unterdrücken
|
||||||
pDocGridView.BestFitColumns(True)
|
Try
|
||||||
|
pDocGridView.BeginUpdate()
|
||||||
|
pDocGridView.OptionsView.BestFitMaxRowCount = -1
|
||||||
|
pDocGridView.BestFitColumns(True)
|
||||||
|
pDocGridView.EndUpdate()
|
||||||
|
Finally
|
||||||
|
_isGridRefreshing = False
|
||||||
|
End Try
|
||||||
End If
|
End If
|
||||||
|
|
||||||
' Alle Spalten aus ReadOnly setzen, danach werden alle passenden auf nicht ReadOnly gesetzt
|
' Alle Spalten aus ReadOnly setzen, danach werden alle passenden auf nicht ReadOnly gesetzt
|
||||||
@@ -570,40 +608,82 @@ Public Class ClassDocGrid
|
|||||||
|
|
||||||
Private Shared Sub gridView_CustomColumnDisplayText(sender As Object, e As CustomColumnDisplayTextEventArgs)
|
Private Shared Sub gridView_CustomColumnDisplayText(sender As Object, e As CustomColumnDisplayTextEventArgs)
|
||||||
Try
|
Try
|
||||||
Dim view As ColumnView = sender
|
' ── Performance-Check 1: Während Refresh nichts tun ──────────────────
|
||||||
|
If _isGridRefreshing Then Return
|
||||||
|
|
||||||
|
' ── Performance-Check 2: Ungültige Row-Handles ignorieren ────────────
|
||||||
|
If e.ListSourceRowIndex = DevExpress.XtraGrid.GridControl.InvalidRowHandle Then
|
||||||
|
Return
|
||||||
|
End If
|
||||||
|
|
||||||
|
' ── Performance-Check 3: Leere Werte schnell verarbeiten ─────────────
|
||||||
|
If e.Value Is Nothing OrElse String.IsNullOrWhiteSpace(e.Value.ToString()) Then
|
||||||
|
e.DisplayText = ""
|
||||||
|
Return
|
||||||
|
End If
|
||||||
|
|
||||||
|
Dim fieldName As String = e.Column.FieldName
|
||||||
Dim parsedDate As DateTime
|
Dim parsedDate As DateTime
|
||||||
If Not IsNothing(DATE_COLUMNS) Then
|
|
||||||
If DATE_COLUMNS.Contains(e.Column.FieldName) And e.ListSourceRowIndex <> DevExpress.XtraGrid.GridControl.InvalidRowHandle Then
|
|
||||||
LOGGER.Debug($"gridView_CustomColumnDisplayText1 [{e.Column.FieldName}] ")
|
|
||||||
If e.Value.ToString() = String.Empty Then
|
|
||||||
e.DisplayText = ""
|
|
||||||
Exit Sub
|
|
||||||
End If
|
|
||||||
|
|
||||||
If Not DateTime.TryParse(e.Value, parsedDate) Then
|
|
||||||
parsedDate = DateTime.ParseExact(e.Value, CURRENT_DATE_FORMAT & " HH:MM:ss", System.Globalization.DateTimeFormatInfo.InvariantInfo)
|
|
||||||
End If
|
|
||||||
|
|
||||||
e.DisplayText = parsedDate.ToString(CURRENT_DATE_FORMAT & " HH:MM:ss")
|
|
||||||
|
|
||||||
|
' ── Datumskonvertierung für Standard-Datumsspalten ───────────────────
|
||||||
|
If DATE_COLUMNS IsNot Nothing AndAlso DATE_COLUMNS.Contains(fieldName) Then
|
||||||
|
' Nur bei Verbose-Logging loggen
|
||||||
|
If EnableVerboseGridLogging Then
|
||||||
|
LOGGER.Debug($"gridView_CustomColumnDisplayText [Standard] [{fieldName}]")
|
||||||
End If
|
End If
|
||||||
End If
|
|
||||||
If Not IsNothing(DATE_COLUMNS_CONFIG) Then
|
|
||||||
If DATE_COLUMNS_CONFIG.Contains(e.Column.FieldName) And e.ListSourceRowIndex <> DevExpress.XtraGrid.GridControl.InvalidRowHandle Then
|
|
||||||
If e.Value.ToString() = String.Empty Then
|
|
||||||
e.DisplayText = ""
|
|
||||||
Exit Sub
|
|
||||||
End If
|
|
||||||
LOGGER.Debug($"gridView_CustomColumnDisplayText2 [{e.Column.FieldName}] ")
|
|
||||||
If Not DateTime.TryParse(e.Value, parsedDate) Then
|
|
||||||
parsedDate = DateTime.ParseExact(e.Value, CURRENT_DATE_FORMAT, System.Globalization.DateTimeFormatInfo.InvariantInfo)
|
|
||||||
End If
|
|
||||||
|
|
||||||
e.DisplayText = parsedDate.ToString(CURRENT_DATE_FORMAT)
|
Try
|
||||||
End If
|
' Versuche direktes Parsen
|
||||||
|
If DateTime.TryParse(e.Value.ToString(), parsedDate) Then
|
||||||
|
e.DisplayText = parsedDate.ToString(CURRENT_DATE_FORMAT & " HH:mm:ss")
|
||||||
|
Else
|
||||||
|
' Fallback: ParseExact
|
||||||
|
parsedDate = DateTime.ParseExact(e.Value.ToString(),
|
||||||
|
CURRENT_DATE_FORMAT & " HH:mm:ss",
|
||||||
|
System.Globalization.DateTimeFormatInfo.InvariantInfo)
|
||||||
|
e.DisplayText = parsedDate.ToString(CURRENT_DATE_FORMAT & " HH:mm:ss")
|
||||||
|
End If
|
||||||
|
Catch ex As FormatException
|
||||||
|
' Bei Parsing-Fehler Original-Wert anzeigen
|
||||||
|
e.DisplayText = e.Value.ToString()
|
||||||
|
If EnableVerboseGridLogging Then
|
||||||
|
LOGGER.Debug($"Date parsing failed for [{fieldName}]: {e.Value}")
|
||||||
|
End If
|
||||||
|
End Try
|
||||||
|
|
||||||
|
Return ' Früher Exit - keine weitere Prüfung nötig
|
||||||
End If
|
End If
|
||||||
|
|
||||||
|
' ── Datumskonvertierung für Config-Datumsspalten ──────────────────────
|
||||||
|
If DATE_COLUMNS_CONFIG IsNot Nothing AndAlso DATE_COLUMNS_CONFIG.Contains(fieldName) Then
|
||||||
|
' Nur bei Verbose-Logging loggen
|
||||||
|
If EnableVerboseGridLogging Then
|
||||||
|
LOGGER.Debug($"gridView_CustomColumnDisplayText [Config] [{fieldName}]")
|
||||||
|
End If
|
||||||
|
|
||||||
|
Try
|
||||||
|
' Versuche direktes Parsen
|
||||||
|
If DateTime.TryParse(e.Value.ToString(), parsedDate) Then
|
||||||
|
e.DisplayText = parsedDate.ToString(CURRENT_DATE_FORMAT)
|
||||||
|
Else
|
||||||
|
' Fallback: ParseExact
|
||||||
|
parsedDate = DateTime.ParseExact(e.Value.ToString(),
|
||||||
|
CURRENT_DATE_FORMAT,
|
||||||
|
System.Globalization.DateTimeFormatInfo.InvariantInfo)
|
||||||
|
e.DisplayText = parsedDate.ToString(CURRENT_DATE_FORMAT)
|
||||||
|
End If
|
||||||
|
Catch ex As FormatException
|
||||||
|
' Bei Parsing-Fehler Original-Wert anzeigen
|
||||||
|
e.DisplayText = e.Value.ToString()
|
||||||
|
If EnableVerboseGridLogging Then
|
||||||
|
LOGGER.Debug($"Date parsing failed for [{fieldName}]: {e.Value}")
|
||||||
|
End If
|
||||||
|
End Try
|
||||||
|
End If
|
||||||
|
|
||||||
Catch ex As Exception
|
Catch ex As Exception
|
||||||
LOGGER.Warn("Unexpected error in gridView_CustomColumnDisplayText: " & ex.Message)
|
' Fehler IMMER loggen (aber nicht Debug)
|
||||||
|
LOGGER.Error($"gridView_CustomColumnDisplayText Error [{e.Column?.FieldName}]: {ex.Message}")
|
||||||
End Try
|
End Try
|
||||||
|
|
||||||
End Sub
|
End Sub
|
||||||
|
|||||||
@@ -386,6 +386,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Classes\ClassConfig.vb" />
|
<Compile Include="Classes\ClassConfig.vb" />
|
||||||
|
<Compile Include="Classes\ClassDataCache.vb" />
|
||||||
<Compile Include="Classes\ClassTreeListViewState.vb" />
|
<Compile Include="Classes\ClassTreeListViewState.vb" />
|
||||||
<Compile Include="Classes\Commands\ClassNodeCommands.vb" />
|
<Compile Include="Classes\Commands\ClassNodeCommands.vb" />
|
||||||
<Compile Include="Classes\NodeNavigation\ClassAsyncNodeBuild.vb" />
|
<Compile Include="Classes\NodeNavigation\ClassAsyncNodeBuild.vb" />
|
||||||
|
|||||||
@@ -34,6 +34,10 @@ Public Class frmWM_IndexFile
|
|||||||
|
|
||||||
|
|
||||||
End Class
|
End Class
|
||||||
|
Private _multiIndexDecisionMade As Boolean = False
|
||||||
|
Private _multiIndexOverwriteExisting As Boolean = False
|
||||||
|
Private _processedFileIds As New List(Of Integer)
|
||||||
|
|
||||||
Function WORK_FILE(ImportFilePath As String, VerzeichnisZiel As String, vDokart_ID As Integer, vDokart As String, multiindex As Boolean)
|
Function WORK_FILE(ImportFilePath As String, VerzeichnisZiel As String, vDokart_ID As Integer, vDokart As String, multiindex As Boolean)
|
||||||
'Dim swWORK_FILE As New SW("WORK_FILE: " & DOCTYPE_IDTextBox.Text)
|
'Dim swWORK_FILE As New SW("WORK_FILE: " & DOCTYPE_IDTextBox.Text)
|
||||||
Try
|
Try
|
||||||
@@ -54,30 +58,64 @@ Public Class frmWM_IndexFile
|
|||||||
End If
|
End If
|
||||||
sw.Done()
|
sw.Done()
|
||||||
sw = New SW("CheckFileExists")
|
sw = New SW("CheckFileExists")
|
||||||
Dim existsonlyasMaster = False
|
|
||||||
|
|
||||||
If WMMOD.TestFileExists(CURRENT_NEWFILENAME) = True Then
|
If WMMOD.TestFileExists(CURRENT_NEWFILENAME) = True Then
|
||||||
Dim msg As String
|
' ── Multi-Index-Logik: Entscheidung nur einmal fragen ─────────
|
||||||
If USER_LANGUAGE <> "de-DE" Then
|
Dim shouldOverwrite As Boolean
|
||||||
msg = "A file with the same name already exists!" & vbCrLf &
|
|
||||||
"YES → Replace the existing file (all former versions will be deleted)" & vbCrLf &
|
If multiindex = True Then
|
||||||
"NO → Keep both files (your file will be versioned automatically)"
|
If _multiIndexDecisionMade = False Then
|
||||||
|
' Erste Kollision → Benutzer fragen
|
||||||
|
Dim msg As String
|
||||||
|
If USER_LANGUAGE <> "de-DE" Then
|
||||||
|
msg = "A file with the same name already exists!" & vbCrLf &
|
||||||
|
"This decision will apply to ALL remaining files in Multi-Indexing." & vbCrLf & vbCrLf &
|
||||||
|
"YES → Replace existing files (all former versions will be deleted)" & vbCrLf &
|
||||||
|
"NO → Keep both files (files will be versioned automatically)"
|
||||||
|
Else
|
||||||
|
msg = "Eine Datei mit identischem Namen existiert bereits!" & vbCrLf &
|
||||||
|
"Diese Entscheidung gilt für ALLE weiteren Dateien im Multi-Indexing." & vbCrLf & vbCrLf &
|
||||||
|
"JA → Bestehende Dateien ersetzen (alle vorherigen Versionen werden dabei gelöscht)" & vbCrLf &
|
||||||
|
"NEIN → Dateien versionieren (beide Dateien werden behalten)"
|
||||||
|
End If
|
||||||
|
|
||||||
|
Dim result As MsgBoxResult = MessageBox.Show(msg, "File already exists (Multi-Indexing):",
|
||||||
|
MessageBoxButtons.YesNo, MessageBoxIcon.Question)
|
||||||
|
|
||||||
|
_multiIndexOverwriteExisting = (result = MsgBoxResult.Yes)
|
||||||
|
_multiIndexDecisionMade = True
|
||||||
|
|
||||||
|
LOGGER.Info($"Multi-Indexing: User decision for file conflicts = {If(_multiIndexOverwriteExisting, "OVERWRITE", "VERSION")}")
|
||||||
|
End If
|
||||||
|
|
||||||
|
shouldOverwrite = _multiIndexOverwriteExisting
|
||||||
Else
|
Else
|
||||||
msg = "Eine Datei mit identischem Namen existiert bereits!" & vbCrLf &
|
' Einzeldatei → wie bisher
|
||||||
"JA → Bestehende Datei ersetzen (alle vorherigen Versionen werden dabei gelöscht)" & vbCrLf &
|
Dim msg As String
|
||||||
"NEIN → Beide Dateien behalten (Ihre Datei wird automatisch versioniert)"
|
If USER_LANGUAGE <> "de-DE" Then
|
||||||
|
msg = "A file with the same name already exists!" & vbCrLf &
|
||||||
|
"YES → Replace the existing file (all former versions will be deleted)" & vbCrLf &
|
||||||
|
"NO → Keep both files (your file will be versioned automatically)"
|
||||||
|
Else
|
||||||
|
msg = "Eine Datei mit identischem Namen existiert bereits!" & vbCrLf &
|
||||||
|
"JA → Bestehende Datei ersetzen (alle vorherigen Versionen werden dabei gelöscht)" & vbCrLf &
|
||||||
|
"NEIN → Beide Dateien behalten (Ihre Datei wird automatisch versioniert)"
|
||||||
|
End If
|
||||||
|
|
||||||
|
Dim result As MsgBoxResult = MessageBox.Show(msg, "File already exists:",
|
||||||
|
MessageBoxButtons.YesNo, MessageBoxIcon.Question)
|
||||||
|
shouldOverwrite = (result = MsgBoxResult.Yes)
|
||||||
End If
|
End If
|
||||||
Dim result As MsgBoxResult
|
|
||||||
result = MessageBox.Show(msg, "File already exists:", MessageBoxButtons.YesNo, MessageBoxIcon.Question)
|
' ── Entscheidung ausführen ────────────────────────────────────
|
||||||
If result = MsgBoxResult.Yes Then
|
If shouldOverwrite Then
|
||||||
If WMMOD.RemoveFile(CURRENT_NEWFILENAME) = False Then
|
If WMMOD.RemoveFile(CURRENT_NEWFILENAME) = False Then
|
||||||
' Löschen fehlgeschlagen → Abbruch
|
|
||||||
Return False
|
Return False
|
||||||
Else
|
Else
|
||||||
Dim oNormPath = WMMOD.GetNormalizedPath(CURRENT_NEWFILENAME, 0)
|
Dim oNormPath = WMMOD.GetNormalizedPath(CURRENT_NEWFILENAME, 0)
|
||||||
|
Dim oGetDoc = String.Format(
|
||||||
Dim oGetDoc = String.Format("select DOC_ID,RECORD_ID from TBPMO_DOC_RECORD_LINK RL INNER JOIN TBPMO_DOCRESULT_LIST DRL WITH (NOLOCK) ON RL.DOC_ID = DRL.DocID
|
"SELECT DOC_ID,RECORD_ID FROM TBPMO_DOC_RECORD_LINK RL " &
|
||||||
WHERE FULL_FILENAME = '{0}'", oNormPath)
|
"INNER JOIN TBPMO_DOCRESULT_LIST DRL WITH (NOLOCK) ON RL.DOC_ID = DRL.DocID " &
|
||||||
|
"WHERE FULL_FILENAME = '{0}'", oNormPath)
|
||||||
Dim oDT As DataTable = MYDB_ECM.GetDatatable(oGetDoc)
|
Dim oDT As DataTable = MYDB_ECM.GetDatatable(oGetDoc)
|
||||||
|
|
||||||
For Each oRow As DataRow In oDT.Rows
|
For Each oRow As DataRow In oDT.Rows
|
||||||
@@ -87,13 +125,10 @@ Public Class frmWM_IndexFile
|
|||||||
}
|
}
|
||||||
ClassFileResult.Delete_ResultFile(oDoc.DocId, oDoc.RecordId, 1)
|
ClassFileResult.Delete_ResultFile(oDoc.DocId, oDoc.RecordId, 1)
|
||||||
Next
|
Next
|
||||||
|
|
||||||
' Löschen erfolgreich → Import wird fortgesetzt
|
|
||||||
End If
|
End If
|
||||||
Else
|
Else
|
||||||
CURRENT_NEWFILENAME = ClassHelper.Versionierung_Datei(CURRENT_NEWFILENAME)
|
CURRENT_NEWFILENAME = ClassHelper.Versionierung_Datei(CURRENT_NEWFILENAME)
|
||||||
End If
|
End If
|
||||||
|
|
||||||
End If
|
End If
|
||||||
sw.Done()
|
sw.Done()
|
||||||
'#################################################################
|
'#################################################################
|
||||||
@@ -220,7 +255,7 @@ Public Class frmWM_IndexFile
|
|||||||
err = True
|
err = True
|
||||||
Exit For
|
Exit For
|
||||||
End If
|
End If
|
||||||
|
_processedFileIds.Add(CURRENT_FILEID)
|
||||||
Next
|
Next
|
||||||
If err = True Then
|
If err = True Then
|
||||||
'swWORK_FILE.Done()
|
'swWORK_FILE.Done()
|
||||||
@@ -389,6 +424,11 @@ Public Class frmWM_IndexFile
|
|||||||
Try
|
Try
|
||||||
Me.Cursor = Cursors.WaitCursor
|
Me.Cursor = Cursors.WaitCursor
|
||||||
SaveMySettingsValue("WD_IndexDeleteDocs", WD_IndexDeleteDocs, "ConfigMain")
|
SaveMySettingsValue("WD_IndexDeleteDocs", WD_IndexDeleteDocs, "ConfigMain")
|
||||||
|
|
||||||
|
' ── Multi-Indexing-Flags zurücksetzen beim Start ──────────────────
|
||||||
|
_multiIndexDecisionMade = False
|
||||||
|
_multiIndexOverwriteExisting = False
|
||||||
|
|
||||||
If chkMultiIndexer.Visible = True And chkMultiIndexer.Checked = True Then
|
If chkMultiIndexer.Visible = True And chkMultiIndexer.Checked = True Then
|
||||||
' ── Erste Datei: FILE_HASH sicherstellen ───────────────────────
|
' ── Erste Datei: FILE_HASH sicherstellen ───────────────────────
|
||||||
For Each oRow As DataRow In CURRENT_TBPMO_FILES_USER.Rows
|
For Each oRow As DataRow In CURRENT_TBPMO_FILES_USER.Rows
|
||||||
@@ -410,7 +450,6 @@ Public Class frmWM_IndexFile
|
|||||||
"AND GUID <> " & firstFileId & " AND USER_WORK = '" & USER_USERNAME & "'")
|
"AND GUID <> " & firstFileId & " AND USER_WORK = '" & USER_USERNAME & "'")
|
||||||
|
|
||||||
If DTFiles2Work Is Nothing OrElse DTFiles2Work.Rows.Count = 0 Then
|
If DTFiles2Work Is Nothing OrElse DTFiles2Work.Rows.Count = 0 Then
|
||||||
' Keine weiteren Dateien – trotzdem Erfolg
|
|
||||||
NEW_FILES_ADDED = True
|
NEW_FILES_ADDED = True
|
||||||
FILE_WORKED = True
|
FILE_WORKED = True
|
||||||
Me.Cursor = Cursors.Default
|
Me.Cursor = Cursors.Default
|
||||||
@@ -478,9 +517,10 @@ Public Class frmWM_IndexFile
|
|||||||
Return False
|
Return False
|
||||||
End If
|
End If
|
||||||
End If
|
End If
|
||||||
Me.Cursor = Cursors.Default
|
|
||||||
Catch ex As Exception
|
Catch ex As Exception
|
||||||
MsgBox("Error in Indexing_File:" & vbCrLf & ex.Message, MsgBoxStyle.Critical)
|
MsgBox("Error in Indexing_File:" & vbCrLf & ex.Message, MsgBoxStyle.Critical)
|
||||||
|
Return False
|
||||||
Finally
|
Finally
|
||||||
Me.Cursor = Cursors.Default
|
Me.Cursor = Cursors.Default
|
||||||
End Try
|
End Try
|
||||||
@@ -1202,24 +1242,14 @@ Public Class frmWM_IndexFile
|
|||||||
|
|
||||||
Private Sub frmWM_IndexFile_Closing(sender As Object, e As CancelEventArgs) Handles Me.Closing
|
Private Sub frmWM_IndexFile_Closing(sender As Object, e As CancelEventArgs) Handles Me.Closing
|
||||||
Try
|
Try
|
||||||
If IsNothing(CURRENT_FILEID) OrElse CURRENT_FILEID.ToString = "" Then
|
' Alle nicht-verarbeiteten Dateien löschen (außer den erfolgreichen)
|
||||||
Exit Sub
|
Dim processedIds = String.Join(",", _processedFileIds)
|
||||||
End If
|
Dim oDelete = $"DELETE FROM TBPMO_FILES_USER WHERE USER_WORK = '{USER_USERNAME}' AND WORKED = 0"
|
||||||
|
If _processedFileIds.Count > 0 Then
|
||||||
' Direkt in der DB prüfen – nicht auf das Flag FILE_WORKED verlassen
|
oDelete &= $" AND GUID NOT IN ({processedIds})"
|
||||||
Dim checkSql = $"SELECT WORKED FROM TBPMO_FILES_USER WHERE GUID = {CURRENT_FILEID}"
|
|
||||||
Dim workedValue = MYDB_ECM.GetScalarValue(checkSql)
|
|
||||||
|
|
||||||
If IsNothing(workedValue) OrElse IsDBNull(workedValue) Then
|
|
||||||
' Datensatz existiert nicht mehr – kein Löschen notwendig
|
|
||||||
Exit Sub
|
|
||||||
End If
|
|
||||||
|
|
||||||
If CBool(workedValue) = False Then
|
|
||||||
Dim oDelete = $"DELETE FROM TBPMO_FILES_USER WHERE GUID = {CURRENT_FILEID} AND WORKED = 0"
|
|
||||||
MYDB_ECM.ExecuteNonQuery(oDelete)
|
|
||||||
LOGGER.Info($"TBPMO_FILES_USER: Eintrag GUID={CURRENT_FILEID} gelöscht (nicht verarbeitet).")
|
|
||||||
End If
|
End If
|
||||||
|
MYDB_ECM.ExecuteNonQuery(oDelete)
|
||||||
|
LOGGER.Info($"TBPMO_FILES_USER: Nicht-verarbeitete Einträge bereinigt (User: {USER_USERNAME})")
|
||||||
Catch ex As Exception
|
Catch ex As Exception
|
||||||
LOGGER.Warn($"Fehler beim Bereinigen von TBPMO_FILES_USER (Closing): {ex.Message}")
|
LOGGER.Warn($"Fehler beim Bereinigen von TBPMO_FILES_USER (Closing): {ex.Message}")
|
||||||
End Try
|
End Try
|
||||||
|
|||||||
Reference in New Issue
Block a user