From 4ba29ce57bd0032317517b6af5225e50852423ca Mon Sep 17 00:00:00 2001 From: Developer01 Date: Fri, 4 Sep 2026 09:28:11 +0200 Subject: [PATCH] Verbesserung der Datenvalidierung und Fehlerbehandlung MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In `ClassCockpit.vb` wurden mehrere Sicherheitsprüfungen und Normalisierungen in der Methode `CreateCockpit` hinzugefügt, einschließlich der Überprüfung auf leere Tabellen, fehlende Spalten und ungültige Primärschlüssel. Duplikate und ungültige `PARENT_ID`-Referenzen werden nun entfernt oder korrigiert. In `frmMonitor.vb` wurde die Fehlerbehandlung beim Laden von Dokumenten verbessert, einschließlich Validierung des Dateipfads und lokalisierter Fehlermeldungen. Ein neues Feld `CFG_SEQUENCE` wurde in `frmCockpitConfig.vb` hinzugefügt, und ein Aufruf zu `AppendCountsToTreeAsync()` wurde in `frmMain.vb` integriert. Neue Ressourcen- und Projektdateien (`ClassCockpit.vb`, `frmMain.aa.resx`) wurden in `taskFLOW.vbproj` hinzugefügt. --- app/TaskFlow/ClassCockpit.vb | 106 ++++++++++++++++++++++--------- app/TaskFlow/frmCockpitConfig.vb | 3 +- app/TaskFlow/frmMain.vb | 2 +- app/TaskFlow/frmMonitor.vb | 75 ++++++++++++++++++---- app/TaskFlow/taskFLOW.vbproj | 4 ++ 5 files changed, 148 insertions(+), 42 deletions(-) diff --git a/app/TaskFlow/ClassCockpit.vb b/app/TaskFlow/ClassCockpit.vb index 293be93..d1e5893 100644 --- a/app/TaskFlow/ClassCockpit.vb +++ b/app/TaskFlow/ClassCockpit.vb @@ -4,6 +4,34 @@ Try LOGGER.Debug("=== TreeView Data Analysis ===") LOGGER.Debug($"Total rows: {pDTNodes.Rows.Count}") + + ' ===== SAFETY CHECKS / NORMALISIERUNGEN ===== + If pDTNodes Is Nothing OrElse pDTNodes.Rows.Count = 0 Then + LOGGER.Warn("CreateCockpit: pDTNodes is Nothing or empty - nothing to bind") + Exit Sub + End If + + ' Prüfen ob Primärschlüssel-Spalte existiert + If Not pDTNodes.Columns.Contains("PK_CFG_COCKPIT_DEFINITION_ID") Then + LOGGER.Error("CreateCockpit: Required column 'PK_CFG_COCKPIT_DEFINITION_ID' is missing - aborting cockpit creation") + Exit Sub + End If + + ' Entferne Zeilen mit NULL/ungültigem PK (können beim Binding zu Duplikat-Keys führen) + Dim rowsWithNullPk As New List(Of DataRow) + For Each r As DataRow In pDTNodes.Rows + If IsDBNull(r("PK_CFG_COCKPIT_DEFINITION_ID")) Then + rowsWithNullPk.Add(r) + End If + Next + If rowsWithNullPk.Count > 0 Then + LOGGER.Warn($"CreateCockpit: Found {rowsWithNullPk.Count} row(s) with NULL PK - removing them to avoid TreeList key errors") + For Each r As DataRow In rowsWithNullPk + r.Delete() + Next + pDTNodes.AcceptChanges() + End If + ' ===== NUR AKTIVE KNOTEN BEHALTEN ===== If pDTNodes.Columns.Contains("ACTIVE") And pAdmin = False Then Dim oInactiveRows As DataRow() = pDTNodes.Select("ACTIVE = 0 OR ACTIVE IS NULL") @@ -42,6 +70,54 @@ End If End If + ' ===== NULL-PARENT_IDS NORMALISIEREN (NULL -> 0) ===== + If pDTNodes.Columns.Contains("PARENT_ID") Then + For Each r As DataRow In pDTNodes.Rows + If IsDBNull(r("PARENT_ID")) Then + r("PARENT_ID") = 0 + End If + Next + Else + ' Falls PARENT_ID fehlt, füge Spalte hinzu und setze 0 + LOGGER.Warn("CreateCockpit: 'PARENT_ID' column missing - adding and setting to 0") + pDTNodes.Columns.Add("PARENT_ID", GetType(Integer)) + For Each r As DataRow In pDTNodes.Rows + r("PARENT_ID") = 0 + Next + End If + pDTNodes.AcceptChanges() + + ' ===== DUPLIKATE PKS ERMITTELN UND ENTFERNEN ===== + Dim dupGroups = pDTNodes.AsEnumerable(). + GroupBy(Function(r) Convert.ToInt32(r("PK_CFG_COCKPIT_DEFINITION_ID"))). + Where(Function(g) g.Count() > 1).ToList() + + If dupGroups.Count > 0 Then + For Each g In dupGroups + LOGGER.Warn($"CreateCockpit: Duplicate PK detected: {g.Key}. Keeping first occurrence, removing {g.Count() - 1} duplicate row(s).") + Dim firstSeen As Boolean = False + For Each rr As DataRow In g + If Not firstSeen Then + firstSeen = True + Continue For + End If + rr.Delete() + Next + Next + pDTNodes.AcceptChanges() + End If + + ' ===== PARENT_IDs REFERENZEN PRÜFEN ===== + Dim existingKeys As New HashSet(Of Integer)(pDTNodes.AsEnumerable().Select(Function(r) Convert.ToInt32(r("PK_CFG_COCKPIT_DEFINITION_ID")))) + For Each r As DataRow In pDTNodes.Rows + Dim pid As Integer = Convert.ToInt32(r("PARENT_ID")) + If pid <> 0 AndAlso Not existingKeys.Contains(pid) Then + LOGGER.Warn($"CreateCockpit: ParentId {pid} referenced by PK {r("PK_CFG_COCKPIT_DEFINITION_ID")} does not exist - setting ParentId to 0") + r("PARENT_ID") = 0 + End If + Next + pDTNodes.AcceptChanges() + Dim rootCount = pDTNodes.AsEnumerable().Count(Function(r) Not IsDBNull(r("PARENT_ID")) AndAlso CInt(r("PARENT_ID")) = 0) LOGGER.Debug($"Root nodes (PARENT_ID = 0): {rootCount}") @@ -67,16 +143,6 @@ ' ===== Jetzt DataSource zuweisen ===== myTreeView.DataSource = pDTNodes - '' ===== DEBUG: ImageList-Inhalt prüfen ===== - 'If myTreeView.SelectImageList IsNot Nothing Then - ' LOGGER.Debug($"SelectImageList hat {myTreeView.SelectImageList.Images.Count} Bilder") - ' For i As Integer = 0 To myTreeView.SelectImageList.Images.Count - 1 - ' LOGGER.Debug($" Image Index {i}: {myTreeView .SelectImageList.Images.Keys(i)}") - ' Next - 'Else - ' LOGGER.Warn("SelectImageList ist Nothing!") - 'End If - AddHandler myTreeView.CustomDrawNodeCell, AddressOf TreeList_Config_CustomDrawNodeCell ' ===== MODERNES DESIGN ===== @@ -134,24 +200,6 @@ LOGGER.Warn("Load_Treeview: No overview nodes (TYPE_ID=0) found - keeping default collapsed state") End If - '' ===== DETAILLIERTES LOGGING ===== - 'LOGGER.Debug("=== DETAILED NODE STRUCTURE AFTER SETUP ===") - 'LOGGER.Debug($"TreeList State - AllNodesCount: {TreeList_Cockpit.AllNodesCount}, RootNodes: {TreeList_Cockpit.Nodes.Count}") - - 'Dim allNodesList As New List(Of DevExpress.XtraTreeList.Nodes.TreeListNode) - 'CollectAllNodes(TreeList_Cockpit.Nodes, allNodesList) - - 'LOGGER.Debug($"Total nodes collected recursively: {allNodesList.Count}") - - 'For Each node As DevExpress.XtraTreeList.Nodes.TreeListNode In allNodesList - ' Dim nodeId = node.GetValue("PK_CFG_COCKPIT_DEFINITION_ID") - ' Dim nodeName = node.GetValue("NAME") - ' Dim nodeParent = node.GetValue("PARENT_ID") - ' Dim level = node.Level - ' Dim parentInfo = If(node.ParentNode Is Nothing, "NULL", $"ID={node.ParentNode.GetValue("PK_CFG_COCKPIT_DEFINITION_ID")}") - - ' LOGGER.Debug($" [Level {level}] ID={nodeId}, NAME={nodeName}, PARENT_ID={nodeParent}, ParentNode={parentInfo}, HasChildren={node.HasChildren}, ChildCount={node.Nodes.Count}, IsExpanded={node.Expanded}") - 'Next Catch ex As Exception LOGGER.Error($"Error in CreateCockpit: {ex.Message}") End Try @@ -186,4 +234,4 @@ LOGGER.Error(ex) End Try End Sub -End Class +End Class \ No newline at end of file diff --git a/app/TaskFlow/frmCockpitConfig.vb b/app/TaskFlow/frmCockpitConfig.vb index 6867570..3f703d9 100644 --- a/app/TaskFlow/frmCockpitConfig.vb +++ b/app/TaskFlow/frmCockpitConfig.vb @@ -895,7 +895,8 @@ Public Class frmCockpitConfig ' ===== SQL-FELDER IM TREEVIEW-KNOTEN AKTUALISIEREN ===== currentNode.SetValue("SQL_QUERY", _currentCockpitDefinition.SQL_Query) currentNode.SetValue("SQL_QUERY_COUNT", _currentCockpitDefinition.SQL_Query_Count) - LOGGER.Debug($"TreeView SQL fields updated - SQL_Query length: {_currentCockpitDefinition.SQL_Query.Length}, SQL_Query_Count length: {_currentCockpitDefinition.SQL_Query_Count.Length}") + currentNode.SetValue("CFG_SEQUENCE", _currentCockpitDefinition.CFG_Sequence) + LOGGER.Debug($"TreeView SQL fields updated - SQL_Query length: {_currentCockpitDefinition.SQL_Query.Length}, SQL_Query_Count length: {_currentCockpitDefinition.SQL_Query_Count.Length}") ' TreeView visuell aktualisieren TreeList_Cockpit.RefreshNode(currentNode) diff --git a/app/TaskFlow/frmMain.vb b/app/TaskFlow/frmMain.vb index 79a30c2..af4f715 100644 --- a/app/TaskFlow/frmMain.vb +++ b/app/TaskFlow/frmMain.vb @@ -564,7 +564,7 @@ Public Class frmMain End If Dim clsck As New ClassCockpit() clsck.CreateCockpit(oDT_STRUCTURE_NODES, TreeList_Cockpit, False) - + AppendCountsToTreeAsync() Catch ex As Exception LOGGER.Error(ex) Finally diff --git a/app/TaskFlow/frmMonitor.vb b/app/TaskFlow/frmMonitor.vb index c1bb7d6..03ef936 100644 --- a/app/TaskFlow/frmMonitor.vb +++ b/app/TaskFlow/frmMonitor.vb @@ -182,11 +182,25 @@ Public Class frmMonitor '_frmDocView.LoadFile(oDocPath) If File.Exists(oDocPath) Then - DocumentViewerMonitor.LoadFile_FromPath(oDocPath) - BarStaticItemInfo.Visibility = DevExpress.XtraBars.BarItemVisibility.Never - DocumentViewerMonitor.Visible = True - ' DocumentViewerMonitor.RightOnlyView(USER_RIGHT_VIEW_ONLY) - + Try + If DocumentViewerMonitor Is Nothing Then + LOGGER.Error("DocumentViewerMonitor is Nothing in Load_Doc") + BarStaticItemInfo.Visibility = DevExpress.XtraBars.BarItemVisibility.Always + BarStaticItemInfo.Caption = "DocView not initialized" + DocumentViewerMonitor.Visible = False + Else + DocumentViewerMonitor.LoadFile_FromPath(oDocPath) + BarStaticItemInfo.Visibility = DevExpress.XtraBars.BarItemVisibility.Never + DocumentViewerMonitor.Visible = True + ' DocumentViewerMonitor.RightOnlyView(USER_RIGHT_VIEW_ONLY) + End If + Catch ex As Exception + LOGGER.Error(ex) + BarStaticItemInfo.Visibility = DevExpress.XtraBars.BarItemVisibility.Always + BarStaticItemInfo.Caption = "Error loading document" + DocumentViewerMonitor.Visible = False + MsgBox(ex.Message, MsgBoxStyle.Critical, "Error in LoadDoc:") + End Try Else BarStaticItemInfo.Visibility = DevExpress.XtraBars.BarItemVisibility.Always BarStaticItemInfo.Caption = $"File is not existing or accessible!" @@ -459,16 +473,55 @@ Public Class frmMonitor End If BarButtonItem3.Down = True - Dim oDocPath + Dim oDocPath As String = String.Empty Try - oDocPath = GridView1.GetFocusedRowCellValue(GridView1.Columns("FULL_FILE_PATH")) - - + oDocPath = Convert.ToString(GridView1.GetFocusedRowCellValue(GridView1.Columns("FULL_FILE_PATH"))) Catch ex As Exception LOGGER.Warn($"Error getting DocPath in Monitor: {ex.Message}") - Exit Sub + oDocPath = String.Empty + End Try + + ' Validierungen einbauen, damit LoadFile_FromPath keine unhandled NullReferenceException wirft + If String.IsNullOrWhiteSpace(oDocPath) Then + LOGGER.Warn("BarButtonItem3: No document path available") + BarStaticItemInfo.Visibility = DevExpress.XtraBars.BarItemVisibility.Always + If USER_LANGUAGE = "de-DE" Then + BarStaticItemInfo.Caption = "Kein Dateipfad verfügbar" + Else + BarStaticItemInfo.Caption = "No file path available" + End If + Exit Sub + End If + + If Not File.Exists(oDocPath) Then + LOGGER.Warn($"BarButtonItem3: File does not exist: {oDocPath}") + BarStaticItemInfo.Visibility = DevExpress.XtraBars.BarItemVisibility.Always + If USER_LANGUAGE = "de-DE" Then + BarStaticItemInfo.Caption = "Datei nicht vorhanden oder nicht zugreifbar" + Else + BarStaticItemInfo.Caption = "File is not existing or accessible!" + End If + If DocumentViewerMonitor IsNot Nothing Then + DocumentViewerMonitor.Visible = False + End If + Exit Sub + End If + + If DocumentViewerMonitor Is Nothing Then + LOGGER.Error("BarButtonItem3: DocumentViewerMonitor is Nothing") + MsgBox("Document viewer is not initialized.", MsgBoxStyle.Critical, "DocView") + Exit Sub + End If + + Try + DocumentViewerMonitor.LoadFile_FromPath(oDocPath) + Catch ex As Exception + LOGGER.Error(ex) + BarStaticItemInfo.Visibility = DevExpress.XtraBars.BarItemVisibility.Always + BarStaticItemInfo.Caption = "Error loading document" + DocumentViewerMonitor.Visible = False + MsgBox(ex.Message, MsgBoxStyle.Critical, "Error loading document") End Try - DocumentViewerMonitor?.LoadFile_FromPath(oDocPath) 'If oDocPath <> "" Then ' If Not Application.OpenForms().OfType(Of frmValidator).Any Then diff --git a/app/TaskFlow/taskFLOW.vbproj b/app/TaskFlow/taskFLOW.vbproj index 37f97ac..1304ac0 100644 --- a/app/TaskFlow/taskFLOW.vbproj +++ b/app/TaskFlow/taskFLOW.vbproj @@ -479,6 +479,7 @@ + @@ -920,6 +921,9 @@ frmLoginAdmin.vb Designer + + frmMain.vb + frmMain.vb Designer