Neue .resx-Dateien und Projektkonfigurationen hinzugefügt

Die Änderungen umfassen das Hinzufügen von neuen `.resx`-Dateien für verschiedene Formulare, die Ressourcen wie Bilder und Texte enthalten. XML-Schemata und Metadaten wurden hinzugefügt, um die Struktur der Ressourcen zu definieren. Neue `.vbproj`-Dateien wurden hinzugefügt, die die Kompilierungs- und Build-Konfigurationen spezifizieren, einschließlich Referenzen zu DevExpress-Komponenten und anderen Bibliotheken. Eingebettete Ressourcen und Projektverweise wurden ebenfalls hinzugefügt. Build-Ziele und Bedingungen wurden definiert, um die Einbindung aller erforderlichen NuGet-Pakete sicherzustellen.

Verbesserungen und Caching-Updates

In `MSSQLServer.vb` wurde `NOLOCK` zur SQL-Abfrage hinzugefügt, um die Leistung zu verbessern. Die Assembly-Versionen in `AssemblyInfo.vb` wurden aktualisiert. In `DatabaseWithFallback.vb` wurden umfangreiche Änderungen vorgenommen, darunter die Einführung von Caches und Unterstützung für URI-Cache-Syntax. Einige Projekte wurden aus `Modules.sln` entfernt. DevExpress-Referenzen in `Patterns.vbproj` wurden aktualisiert. Eine Backup-Datei `Patterns.vbproj.bak` wurde hinzugefügt.
This commit is contained in:
Developer01
2026-10-22 18:06:18 +02:00
parent 3eb6314ba8
commit fb6d27e53b
7 changed files with 516 additions and 52 deletions

View File

@@ -178,7 +178,7 @@ Public Class MSSQLServer
End If
Try
Dim oTable As DataTable = GetDatatable($"SELECT * FROM TBDD_CONNECTION WHERE GUID = {pConnectionId}")
Dim oTable As DataTable = GetDatatable($"SELECT * FROM TBDD_CONNECTION WITH (NOLOCK) WHERE GUID = {pConnectionId}")
If oTable.Rows.Count = 1 Then
Dim oRow As DataRow = oTable.Rows(0)
Dim oProvider = oRow.Item("SQL_PROVIDER").ToString.ToUpper

View File

@@ -13,7 +13,7 @@ Imports System.Runtime.InteropServices
<Assembly: AssemblyCompany("Digital Data")>
<Assembly: AssemblyProduct("Modules.Database")>
<Assembly: AssemblyCopyright("Copyright © 2025")>
<Assembly: AssemblyTrademark("2.4.0.0")>
<Assembly: AssemblyTrademark("2.5.0.0")>
<Assembly: ComVisible(False)>
@@ -31,5 +31,5 @@ Imports System.Runtime.InteropServices
' übernehmen, indem Sie "*" eingeben:
' <Assembly: AssemblyVersion("1.0.*")>
<Assembly: AssemblyVersion("2.4.0.0")>
<Assembly: AssemblyVersion("2.5.0.0")>
<Assembly: AssemblyFileVersion("2.4.0.0")>

View File

@@ -1,4 +1,5 @@
Imports System.Data.SqlClient
Imports System.Collections.Concurrent
Imports System.Data.SqlClient
Imports DigitalData.Modules.Database
Imports DigitalData.Modules.EDMI.API
Imports DigitalData.Modules.EDMI.API.Constants
@@ -11,7 +12,70 @@ Public Class DatabaseWithFallback
Private ReadOnly _ClientConfig As GlobalStateClientConfiguration
Private ReadOnly _DatabaseECM As MSSQLServer
Private _DatabaseIDB As MSSQLServer
''' <summary>
''' Lokaler Override-Cache für ExecuteAppServiceWrite-Änderungen.
''' Schlüssel: "TABELLENNAME|FILTERAUSDRUCK" → geänderter DataTable-Snapshot.
''' Wird beim nächsten Server-Refresh (Client.UpdateTimer) automatisch obsolet.
''' </summary>
Private ReadOnly _localOverrideCache As New ConcurrentDictionary(Of String, DataTable)()
''' <summary>Erstellt den Cache-Key aus Tabellenname und Filter.</summary>
Private Shared Function GetOverrideCacheKey(pTable As String, pFilter As String) As String
Return $"{pTable.ToUpper}|{pFilter}"
End Function
''' <summary>Leert den lokalen Override-Cache (z.B. nach manuellem Refresh).</summary>
Public Sub InvalidateLocalOverrideCache()
_localOverrideCache.Clear()
_Logger.Debug("InvalidateLocalOverrideCache: Lokaler Override-Cache geleert.")
End Sub
''' <summary>Leert den Override-Cache für eine bestimmte Tabelle.</summary>
Public Sub InvalidateLocalOverrideCache(pTableName As String)
Dim oKeysToRemove = _localOverrideCache.Keys.
Where(Function(k) k.StartsWith(pTableName.ToUpper & "|")).ToList()
For Each oKey In oKeysToRemove
Dim oRemoved As DataTable = Nothing
_localOverrideCache.TryRemove(oKey, oRemoved)
Next
_Logger.Debug("InvalidateLocalOverrideCache: Override-Cache für Tabelle [{0}] geleert.", pTableName)
End Sub
''' <summary>
''' Thread-sicherer Cache für Connection Strings, indexiert nach Connection-ID.
''' Verhindert wiederholte DB-Roundtrips für SELECT * FROM TBDD_CONNECTION.
''' </summary>
Private ReadOnly _connectionStringCache As New ConcurrentDictionary(Of Integer, String)
''' <summary>
''' Liefert den Connection String für eine bekannte Connection-ID.
''' Beim ersten Aufruf wird der Wert aus der DB geladen und gecacht.
''' Hinweis: Bei parallelen Erstabrufen derselben ID kann die Factory mehrfach
''' aufgerufen werden – da der Wert idempotent ist, ist dies funktional korrekt.
''' </summary>
Private Function GetCachedConnectionString(pConnectionId As Integer) As String
Return _connectionStringCache.GetOrAdd(pConnectionId,
Function(id)
_Logger.Debug("GetCachedConnectionString: Cache miss für ConnectionId [{0}] - lade aus DB", id)
Dim oConnectionString = _DatabaseECM.Get_ConnectionStringforID(id)
_Logger.Debug("GetCachedConnectionString: ConnectionId [{0}] gecacht", id)
Return oConnectionString
End Function)
End Function
''' <summary>
''' Leert den Connection-String-Cache, z.B. nach Konfigurationsänderungen.
''' </summary>
Public Sub InvalidateConnectionCache()
_connectionStringCache.Clear()
_Logger.Debug("InvalidateConnectionCache: Connection-String-Cache geleert")
End Sub
''' <summary>
''' Leert den Cache für eine bestimmte Connection-ID.
''' </summary>
Public Sub InvalidateConnectionCache(pConnectionId As Integer)
Dim removed As String = Nothing
If _connectionStringCache.TryRemove(pConnectionId, removed) Then
_Logger.Debug("InvalidateConnectionCache: ConnectionId [{0}] aus Cache entfernt", pConnectionId)
End If
End Sub
''' <summary>
''' Options for GetDatatable
''' </summary>
@@ -35,7 +99,105 @@ Public Class DatabaseWithFallback
''' Connection Id to use, references TBDD_CONNECTION
''' </summary>
Public Property ConnectionId As Integer = 0
''' <summary>
''' Erkennt URI-Cache-Syntax: "TABELLENNAME?FILTERAUSDRUCK"
''' Beispiel: "TBDD_USER?USER_ID=4524"
''' </summary>
Public ReadOnly Property IsCacheQuery As Boolean
Get
If String.IsNullOrWhiteSpace(FallbackSQL) Then Return False
Dim oTrimmed As String = FallbackSQL.TrimStart()
' Nur wenn kein SQL-Schlüsselwort vorhanden → Cache-Routing-Syntax
Return FallbackSQL.Contains("?") AndAlso
Not oTrimmed.StartsWith("SELECT", StringComparison.OrdinalIgnoreCase) AndAlso
Not oTrimmed.StartsWith("INSERT", StringComparison.OrdinalIgnoreCase) AndAlso
Not oTrimmed.StartsWith("UPDATE", StringComparison.OrdinalIgnoreCase) AndAlso
Not oTrimmed.StartsWith("DELETE", StringComparison.OrdinalIgnoreCase) AndAlso
Not oTrimmed.Contains("DECLARE") AndAlso
Not oTrimmed.StartsWith("EXEC", StringComparison.OrdinalIgnoreCase) AndAlso
Not oTrimmed.StartsWith("DECLARE", StringComparison.OrdinalIgnoreCase)
End Get
End Property
Public ReadOnly Property IsCacheWriteQuery As Boolean
Get
If String.IsNullOrWhiteSpace(FallbackSQL) Then Return False
Dim oTrimmed As String = FallbackSQL.TrimStart()
Return FallbackSQL.Contains("?") AndAlso
FallbackSQL.Contains("!") AndAlso
Not oTrimmed.StartsWith("SELECT", StringComparison.OrdinalIgnoreCase) AndAlso
Not oTrimmed.StartsWith("INSERT", StringComparison.OrdinalIgnoreCase) AndAlso
Not oTrimmed.StartsWith("UPDATE", StringComparison.OrdinalIgnoreCase) AndAlso
Not oTrimmed.StartsWith("DELETE", StringComparison.OrdinalIgnoreCase) AndAlso
Not oTrimmed.StartsWith("EXEC", StringComparison.OrdinalIgnoreCase) AndAlso
Not oTrimmed.StartsWith("DECLARE", StringComparison.OrdinalIgnoreCase) AndAlso
Not oTrimmed.StartsWith("--", StringComparison.OrdinalIgnoreCase) AndAlso
Not oTrimmed.StartsWith("/*", StringComparison.OrdinalIgnoreCase)
End Get
End Property
''' <summary>"TBDD_USER?USER_ID=4524" → "TBDD_USER"</summary>
Public ReadOnly Property CacheQueryTable As String
Get
If Not IsCacheQuery Then Return String.Empty
Return FallbackSQL.Substring(0, FallbackSQL.IndexOf("?")).Trim()
End Get
End Property
''' <summary>"TBDD_USER?USER_ID=4524" → "USER_ID=4524"</summary>
Public ReadOnly Property CacheQueryFilter As String
Get
If Not IsCacheQuery Then Return String.Empty
Return FallbackSQL.Substring(FallbackSQL.IndexOf("?") + 1).Trim()
End Get
End Property
''' <summary>"TBDD_USER?USER_ID=4524!STATUS=1" → "TBDD_USER"</summary>
Public ReadOnly Property CacheWriteTable As String
Get
If Not IsCacheWriteQuery Then Return String.Empty
Return FallbackSQL.Substring(0, FallbackSQL.IndexOf("?")).Trim()
End Get
End Property
''' <summary>"TBDD_USER?USER_ID=4524!STATUS=1" → "USER_ID=4524"</summary>
Public ReadOnly Property CacheWriteFilter As String
Get
If Not IsCacheWriteQuery Then Return String.Empty
Dim oStart As Integer = FallbackSQL.IndexOf("?") + 1
Dim oLength As Integer = FallbackSQL.IndexOf("!") - oStart
Return FallbackSQL.Substring(oStart, oLength).Trim()
End Get
End Property
''' <summary>"TBDD_USER?USER_ID=4524!STATUS=1" → "STATUS=1" | "DELETE"</summary>
Public ReadOnly Property CacheWriteAction As String
Get
If Not IsCacheWriteQuery Then Return String.Empty
Return FallbackSQL.Substring(FallbackSQL.IndexOf("!") + 1).Trim()
End Get
End Property
''' <summary>True wenn die Aktion ein DELETE ist</summary>
Public ReadOnly Property CacheWriteIsDelete As Boolean
Get
Return IsCacheWriteQuery AndAlso
CacheWriteAction.Equals("DELETE", StringComparison.OrdinalIgnoreCase)
End Get
End Property
''' <summary>
''' Generiert das SQL aus der URI-Write-Syntax.
''' UPDATE: "TBDD_USER?USER_ID=4524!STATUS=1" → "UPDATE TBDD_USER SET STATUS=1 WHERE USER_ID=4524"
''' DELETE: "TBDD_USER?USER_ID=4524!DELETE" → "DELETE FROM TBDD_USER WHERE USER_ID=4524"
''' </summary>
Public ReadOnly Property CacheWriteToSql As String
Get
If Not IsCacheWriteQuery Then Return String.Empty
If CacheWriteIsDelete Then
Return $"DELETE FROM {CacheWriteTable} WHERE {CacheWriteFilter}"
Else
Return $"UPDATE {CacheWriteTable} SET {CacheWriteAction} WHERE {CacheWriteFilter}"
End If
End Get
End Property
''' <summary>
''' Creates a new options object for GetDatatable options
''' </summary>
@@ -45,8 +207,19 @@ Public Class DatabaseWithFallback
FallbackSQL = pFallbackSQL
FallbackType = pFallbackType
End Sub
''' <summary>
''' Creates a new options object using URI-Syntax (Cache-Query or URI-Write-Query).
''' FallbackType defaults to ECM – only relevant if URI detection fails and falls back to SQL.
''' Beispiel Read: "TBDD_USER?USER_ID=4524"
''' Beispiel Write: "TBDD_USER?USER_ID=4524!STATUS=1"
''' Beispiel Write: "TBDD_USER?USER_ID=4524!DELETE"
''' </summary>
''' <param name="pURISyntax">URI-Syntax string</param>
Public Sub New(pURISyntax As String)
FallbackSQL = pURISyntax
FallbackType = Constants.DatabaseType.ECM
End Sub
End Class
Public Sub New(LogConfig As LogConfig, Client As Client, DatabaseECM As MSSQLServer, DatabaseIDB As MSSQLServer)
_Logger = LogConfig.GetLogger()
_Client = Client
@@ -73,7 +246,9 @@ Public Class DatabaseWithFallback
Return _DatabaseECM.GetConnectionStringForId(pConnectionId)
End Function
#Region "GetDatatable"
''' <summary>URI-Cache-Syntax wird über GetDatatable(pOptions) ausgewertet.</summary>
Public Function GetDatatableECM(pSQL As String) As DataTable
Return GetDatatable(New GetDatatableOptions(pSQL, Constants.DatabaseType.ECM))
End Function
@@ -82,6 +257,7 @@ Public Class DatabaseWithFallback
Return Await GetDatatableFromServiceAsync(pSQL, Constants.DatabaseType.ECM, 0)
End Function
''' <summary>URI-Cache-Syntax wird über GetDatatable(pOptions) ausgewertet.</summary>
Public Function GetDatatableIDB(pSQL As String) As DataTable
Return GetDatatable(New GetDatatableOptions(pSQL, Constants.DatabaseType.IDB))
End Function
@@ -91,7 +267,12 @@ Public Class DatabaseWithFallback
End Function
Public Function GetDatatableWithConnection(pSQL As String, pConnectionId As Integer) As DataTable
Return GetDatatable(New GetDatatableOptions(pSQL, Constants.DatabaseType.None) With {.ConnectionId = pConnectionId})
' ===== URI-CACHE-SYNTAX: "TABELLENNAME?FILTERAUSDRUCK" =====
Dim oOptions As New GetDatatableOptions(pSQL, Constants.DatabaseType.None) With {.ConnectionId = pConnectionId}
If oOptions.IsCacheQuery Then
Return GetDatatable(oOptions.CacheQueryTable, oOptions)
End If
Return GetDatatable(oOptions)
End Function
Public Async Function GetDatatableWithConnectionAsync(pSQL As String, pConnectionId As Integer) As Task(Of DataTable)
@@ -126,6 +307,9 @@ Public Class DatabaseWithFallback
#End Region
#Region "ExecuteNonQuery"
' URI-Cache-Syntax ("TABELLE?FILTER") ist hier nicht anwendbar:
' ExecuteNonQuery führt schreibende Operationen aus (INSERT/UPDATE/DELETE)
' und liefert kein Ergebnis, das aus dem AppServer-Cache gelesen werden könnte.
Public Function ExecuteNonQueryECM(pSQL As String) As Boolean
Return ExecuteNonQuery(pSQL, Constants.DatabaseType.ECM)
End Function
@@ -158,18 +342,61 @@ Public Class DatabaseWithFallback
''' <param name="pDataTableName">Name of the Cached Datatable</param>
''' <param name="pOptions">Options object</param>
Public Function GetDatatable(pDataTableName As String, pOptions As GetDatatableOptions) As DataTable
' ===== URI-CACHE-SYNTAX: "TABELLENNAME?FILTERAUSDRUCK" =====
If pOptions.IsCacheQuery AndAlso Not pOptions.ForceFallback AndAlso
Not (_ClientConfig IsNot Nothing AndAlso _ClientConfig.ForceDirectDatabaseAccess) Then
Dim oTable As String = pOptions.CacheQueryTable
Dim oFilter As String = pOptions.CacheQueryFilter
' ===== LOKALER OVERRIDE-CACHE – vorrangig prüfen =====
Dim oCacheKey As String = GetOverrideCacheKey(oTable, oFilter)
Dim oOverride As DataTable = Nothing
If _localOverrideCache.TryGetValue(oCacheKey, oOverride) Then
_Logger.Debug("✔ GetDatatable: Lokaler Override-Cache trifft für [{0}] Filter [{1}]", oTable, oFilter)
Return oOverride
End If
_Logger.Debug("GetDatatable: URI-Cache-Query erkannt – Tabelle [{0}] Filter [{1}]", oTable, oFilter)
If _Client IsNot Nothing AndAlso IsTableCached(oTable) Then
Try
Dim oResult As TableResult = _Client.GetDatatableByName(oTable, oFilter, pOptions.SortByColumn)
If oResult IsNot Nothing AndAlso oResult.OK Then
_Logger.Debug("✔ GetDatatable: URI-Cache-Query [{0}] – {1} Zeilen", oTable, oResult.Table?.Rows.Count)
Return oResult.Table
End If
Catch ex As Exception
_Logger.Error(ex)
End Try
_Logger.Warn("⚠ GetDatatable: URI-Cache-Query [{0}] fehlgeschlagen – kein SQL-Fallback verfügbar", oTable)
Return Nothing
End If
_Logger.Warn("⚠ GetDatatable: URI-Cache-Query – Tabelle [{0}] nicht im AppServer-Cache", oTable)
Return Nothing
End If
Return GetDatatable(pDataTableName, pOptions.FallbackSQL, pOptions.FallbackType, pOptions.FilterExpression, pOptions.SortByColumn, pOptions.ForceFallback, pOptions.ConnectionId)
End Function
Public Function GetDatatable(pSQL As String, pConnectionId As Integer) As DataTable
' ===== URI-CACHE-SYNTAX: "TABELLENNAME?FILTERAUSDRUCK" =====
Dim oOptions As New GetDatatableOptions(pSQL, Constants.DatabaseType.ECM) With {.ConnectionId = pConnectionId}
If oOptions.IsCacheQuery Then
Return GetDatatable(oOptions.CacheQueryTable, oOptions)
End If
Return GetDatatable("FORCE_FALLBACK", pSQL, Constants.DatabaseType.ECM, pSortByColumn:=Nothing, pForceFallback:=False, pConnectionId:=pConnectionId)
End Function
''' <summary>
''' Returns a datatable directly from the database.
''' Returns a datatable directly from the database – or from AppServer cache if URI-Cache-Syntax is detected.
''' </summary>
''' <param name="pOptions">Options object</param>
Public Function GetDatatable(pOptions As GetDatatableOptions) As DataTable
' ===== URI-CACHE-SYNTAX: "TABELLENNAME?FILTERAUSDRUCK" =====
If pOptions.IsCacheQuery Then
Return GetDatatable(pOptions.CacheQueryTable, pOptions)
End If
Dim oForceFallback = True
Return GetDatatable("FORCE_FALLBACK", pOptions.FallbackSQL, pOptions.FallbackType, pOptions.FilterExpression, pOptions.SortByColumn, oForceFallback, pOptions.ConnectionId)
End Function
@@ -186,7 +413,6 @@ Public Class DatabaseWithFallback
''' <param name="pConnectionId">Connection Id to use, references TBDD_CONNECTION</param>
Public Function GetDatatable(pDataTableName As String, pFallbackSQL As String, pFallbackType As Constants.DatabaseType, Optional pFilterExpression As String = "", Optional pSortByColumn As String = "", Optional pForceFallback As Boolean = False, Optional pConnectionId As Integer = 0) As DataTable
Try
Dim oResult As DataTable = Nothing
Dim oTableResult As TableResult = Nothing
_Logger.Debug("GetDatatable: Fetching datatable [{0}] , Filter Expression [{1}], Sort By Column [{2}], Force Fallback [{3}]",
pDataTableName, pFilterExpression, pSortByColumn, pForceFallback)
@@ -239,44 +465,66 @@ Public Class DatabaseWithFallback
''' <summary>
''' Returns a Scalar Value by querying the database through the service and querying the database directly as fallback.
''' URI-Cache-Syntax wird unterstützt: "TABELLENNAME?FILTERAUSDRUCK" – liefert Wert der ersten Spalte der ersten Zeile aus dem AppServer-Cache.
''' </summary>
''' <param name="pSQL">SQL Command to execute as fallback</param>
''' <param name="pSQL">SQL Command to execute as fallback – oder URI-Cache-Syntax "TABELLENNAME?FILTERAUSDRUCK"</param>
''' <param name="pDatabaseType">Named Database to use for the fallback SQL Command</param>
''' <param name="pForceFallback">Force the fallback, skipping the service completely</param>
''' <param name="pConnectionId">Connection Id to use, references TBDD_CONNECTION</param>
Public Function GetScalarValue(pSQL As String, pDatabaseType As Constants.DatabaseType, Optional pForceFallback As Boolean = False, Optional pConnectionId As Integer = 0) As Object
Try
' If there is no client, we assume there is no service (configured)
' ===== URI-CACHE-SYNTAX: "TABELLENNAME?FILTERAUSDRUCK" =====
' ForceFallback übersteuert auch die URI-Cache-Syntax
Dim oOptions As New GetDatatableOptions(pSQL, pDatabaseType)
If oOptions.IsCacheQuery AndAlso Not pForceFallback AndAlso
Not (_ClientConfig IsNot Nothing AndAlso _ClientConfig.ForceDirectDatabaseAccess) Then
Dim oTable As String = oOptions.CacheQueryTable
Dim oFilter As String = oOptions.CacheQueryFilter
_Logger.Debug("GetScalarValue: URI-Cache-Query erkannt – Tabelle [{0}] Filter [{1}]", oTable, oFilter)
If _Client IsNot Nothing AndAlso IsTableCached(oTable) Then
Try
Dim oResult As TableResult = _Client.GetDatatableByName(oTable, oFilter, "")
If oResult IsNot Nothing AndAlso oResult.OK AndAlso
oResult.Table IsNot Nothing AndAlso oResult.Table.Rows.Count > 0 Then
Dim oScalar As Object = oResult.Table.Rows(0)(0)
_Logger.Debug("✔ GetScalarValue: URI-Cache-Query [{0}] – Scalar [{1}]", oTable, oScalar)
Return oScalar
End If
Catch ex As Exception
_Logger.Error(ex)
End Try
_Logger.Warn("⚠ GetScalarValue: URI-Cache-Query [{0}] fehlgeschlagen – kein SQL-Fallback verfügbar", oTable)
Return Nothing
End If
_Logger.Warn("⚠ GetScalarValue: URI-Cache-Query – Tabelle [{0}] nicht im AppServer-Cache", oTable)
Return Nothing
End If
If _Client Is Nothing Then
Return GetScalarValueFromDatabase(pSQL, pDatabaseType, pConnectionId)
End If
' If ForceFallback flag is set, we go to database immediately
If pForceFallback Or _ClientConfig.ForceDirectDatabaseAccess Then
Return GetScalarValueFromDatabase(pSQL, pDatabaseType, pConnectionId)
End If
Return GetScalarValueFromService(pSQL, pDatabaseType, pConnectionId)
Catch ex As Exception
_Logger.Error(ex)
Return Nothing
End Try
End Function
''' <summary>
''' Returns a Scalar Value by querying the database through the service and querying the database directly as fallback.
''' Executes a non-query SQL command through the service and falls back to direct database access.
''' </summary>
''' <param name="pSQL">SQL Command to execute as fallback</param>
''' <param name="pDatabaseType">Named Database to use for the fallback SQL Command</param>
''' <param name="pSQL">SQL Command to execute</param>
''' <param name="pDatabaseType">Named Database to use for the SQL Command</param>
''' <param name="pForceFallback">Force the fallback, skipping the service completely</param>
''' <param name="pConnectionId">Connection Id to use, references TBDD_CONNECTION</param>
Public Function ExecuteNonQuery(pSQL As String, pDatabaseType As Constants.DatabaseType, Optional pForceFallback As Boolean = False, Optional pConnectionId As Integer = 0) As Boolean
Try
Dim oResult As DataTable = Nothing
Dim oTableResult As TableResult = Nothing
' If there is no client, we assume there is no service (configured)
If _Client Is Nothing Then
_Logger.Debug("ExecuteNonQuery - Client is empty, falling back to direct database access.")
@@ -293,11 +541,78 @@ Public Class DatabaseWithFallback
Catch ex As Exception
_Logger.Error(ex)
Return Nothing
Return False
End Try
End Function
''' <summary>
''' Führt ein UPDATE oder DELETE über URI-Write-Syntax aus.
''' Der geänderte Snapshot wird lokal gecacht und bei GetDatatable/GetScalarValue
''' vorrangig ausgewertet – bis der Server-Refresh den Cache neu befüllt.
''' UPDATE: "TBDD_USER?USER_ID=4524!STATUS=1"
''' DELETE: "TBDD_USER?USER_ID=4524!DELETE"
''' </summary>
Public Function ExecuteAppServiceWrite(pOptions As GetDatatableOptions) As Boolean
If Not pOptions.IsCacheWriteQuery Then
_Logger.Warn("⚠ ExecuteAppServiceWrite: Keine gültige URI-Write-Syntax – [{0}] – Abbruch.", pOptions.FallbackSQL)
Return False
End If
If _Client Is Nothing OrElse Not IsTableCached(pOptions.CacheWriteTable) Then
_Logger.Warn("⚠ ExecuteAppServiceWrite: Tabelle [{0}] nicht im AppServer-Cache – Abbruch.", pOptions.CacheWriteTable)
Return False
End If
_Logger.Debug("ExecuteAppServiceWrite: Tabelle [{0}] | Filter [{1}] | Aktion [{2}] | IsDelete [{3}]",
pOptions.CacheWriteTable, pOptions.CacheWriteFilter, pOptions.CacheWriteAction, pOptions.CacheWriteIsDelete)
Try
' Aktuellen Snapshot aus Cache laden
Dim oResult As TableResult = _Client.GetDatatableByName(pOptions.CacheWriteTable, pOptions.CacheWriteFilter, "")
If oResult Is Nothing OrElse Not oResult.OK OrElse oResult.Table Is Nothing Then
_Logger.Warn("⚠ ExecuteAppServiceWrite: Cache-Abfrage für [{0}] fehlgeschlagen.", pOptions.CacheWriteTable)
Return False
End If
Dim oMatchingRows As DataRow() = oResult.Table.Select(pOptions.CacheWriteFilter)
If oMatchingRows.Length = 0 Then
_Logger.Debug("ExecuteAppServiceWrite: Keine Zeilen für Filter [{0}] gefunden.", pOptions.CacheWriteFilter)
Return True
End If
If pOptions.CacheWriteIsDelete Then
For Each oRow As DataRow In oMatchingRows
oRow.Delete()
Next
oResult.Table.AcceptChanges()
_Logger.Debug("✔ ExecuteAppServiceWrite: DELETE – [{0}] Zeilen entfernt.", oMatchingRows.Length)
Else
For Each oPair As String In pOptions.CacheWriteAction.Split(","c)
Dim oParts = oPair.Split("="c)
If oParts.Length <> 2 Then Continue For
Dim oCol As String = oParts(0).Trim()
Dim oVal As String = oParts(1).Trim().Trim("'"c)
For Each oRow As DataRow In oMatchingRows
If oResult.Table.Columns.Contains(oCol) Then
oRow(oCol) = oVal
End If
Next
Next
oResult.Table.AcceptChanges()
_Logger.Debug("✔ ExecuteAppServiceWrite: UPDATE – [{0}] Zeilen aktualisiert.", oMatchingRows.Length)
End If
' ===== OVERRIDE-CACHE BEFÜLLEN =====
Dim oCacheKey As String = GetOverrideCacheKey(pOptions.CacheWriteTable, pOptions.CacheWriteFilter)
_localOverrideCache(oCacheKey) = oResult.Table
_Logger.Debug("✔ ExecuteAppServiceWrite: Override-Cache gesetzt für [{0}]", oCacheKey)
Return True
Catch ex As Exception
_Logger.Error(ex)
Return False
End Try
End Function
Private Function IsTableCached(pName As String) As Boolean
_Logger.Debug("IsTableCached: Checking if table [{0}] is cached.", pName)
If _Client Is Nothing Then
@@ -391,7 +706,7 @@ Public Class DatabaseWithFallback
Case Else
If pConnectionId > 0 Then
_Logger.Debug("Retrieving Connection String from Connection Id [{0}]", pConnectionId)
Dim oConnectionString = _DatabaseECM.Get_ConnectionStringforID(pConnectionId)
Dim oConnectionString = GetCachedConnectionString(pConnectionId) ' ← NEU: Cache statt direkter DB-Abfrage
Return _DatabaseECM.GetDatatableWithConnection(pSQLCommand, oConnectionString)
Else
Return _DatabaseECM.GetDatatable(pSQLCommand)
@@ -484,7 +799,7 @@ Public Class DatabaseWithFallback
Case Else
If pConnectionId > 0 Then
Dim oConnectionString = _DatabaseECM.Get_ConnectionStringforID(pConnectionId)
Dim oConnectionString = GetCachedConnectionString(pConnectionId) ' ← NEU: Cache statt direkter DB-Abfrage
Return _DatabaseECM.GetScalarValueWithConnection(pSQLCommand, oConnectionString)
Else
Return _DatabaseECM.GetScalarValue(pSQLCommand)
@@ -577,7 +892,7 @@ Public Class DatabaseWithFallback
Case Else
If pConnectionId > 0 Then
Dim oConnectionString = _DatabaseECM.Get_ConnectionStringforID(pConnectionId)
Dim oConnectionString = GetCachedConnectionString(pConnectionId) ' ← NEU: Cache statt direkter DB-Abfrage
Return _DatabaseECM.ExecuteNonQueryWithConnection(pSQLCommand, oConnectionString)
Else
Return _DatabaseECM.ExecuteNonQuery(pSQLCommand)

View File

@@ -13,7 +13,7 @@ Imports System.Runtime.InteropServices
<Assembly: AssemblyCompany("Digital Data")>
<Assembly: AssemblyProduct("EDMIAPI")>
<Assembly: AssemblyCopyright("Copyright © 2026")>
<Assembly: AssemblyTrademark("2.8.0")>
<Assembly: AssemblyTrademark("2.9.0")>
<Assembly: ComVisible(False)>
@@ -31,5 +31,5 @@ Imports System.Runtime.InteropServices
' übernehmen, indem Sie "*" eingeben:
' <Assembly: AssemblyVersion("1.0.*")>
<Assembly: AssemblyVersion("2.8.0.0")>
<Assembly: AssemblyVersion("2.9.0.0")>
<Assembly: AssemblyFileVersion("1.6.1.1")>

View File

@@ -24,8 +24,6 @@ Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "License", "License\License.
EndProject
Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Logging", "Logging\Logging.vbproj", "{903B2D7D-3B80-4BE9-8713-7447B704E1B0}"
EndProject
Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Logging.Test", "Logging.Test\Logging.Test.vbproj", "{3207D8E7-36E3-4714-9B03-7B5B3D6D351A}"
EndProject
Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Patterns", "Patterns\Patterns.vbproj", "{7C3B0C7E-59FE-4E1A-A655-27AE119F9444}"
EndProject
Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Windows", "Windows\Windows.vbproj", "{5EFAEF9B-90B9-4F05-9F70-F79AD77FFF86}"
@@ -49,63 +47,45 @@ Global
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{6EA0C51F-C2B1-4462-8198-3DE0B32B74F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6EA0C51F-C2B1-4462-8198-3DE0B32B74F8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6EA0C51F-C2B1-4462-8198-3DE0B32B74F8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6EA0C51F-C2B1-4462-8198-3DE0B32B74F8}.Release|Any CPU.Build.0 = Release|Any CPU
{44982F9B-6116-44E2-85D0-F39650B1EF99}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{44982F9B-6116-44E2-85D0-F39650B1EF99}.Debug|Any CPU.Build.0 = Debug|Any CPU
{44982F9B-6116-44E2-85D0-F39650B1EF99}.Release|Any CPU.ActiveCfg = Release|Any CPU
{44982F9B-6116-44E2-85D0-F39650B1EF99}.Release|Any CPU.Build.0 = Release|Any CPU
{EAF0EA75-5FA7-485D-89C7-B2D843B03A96}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EAF0EA75-5FA7-485D-89C7-B2D843B03A96}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EAF0EA75-5FA7-485D-89C7-B2D843B03A96}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EAF0EA75-5FA7-485D-89C7-B2D843B03A96}.Release|Any CPU.Build.0 = Release|Any CPU
{25017513-0D97-49D3-98D7-BA76D9B251B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{25017513-0D97-49D3-98D7-BA76D9B251B0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{25017513-0D97-49D3-98D7-BA76D9B251B0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{25017513-0D97-49D3-98D7-BA76D9B251B0}.Release|Any CPU.Build.0 = Release|Any CPU
{8A8F20FC-C46E-41AC-BEE7-218366CFFF99}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8A8F20FC-C46E-41AC-BEE7-218366CFFF99}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8A8F20FC-C46E-41AC-BEE7-218366CFFF99}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8A8F20FC-C46E-41AC-BEE7-218366CFFF99}.Release|Any CPU.Build.0 = Release|Any CPU
{AB6F09BF-E794-4F6A-94BB-C97C0BA84D64}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AB6F09BF-E794-4F6A-94BB-C97C0BA84D64}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AB6F09BF-E794-4F6A-94BB-C97C0BA84D64}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AB6F09BF-E794-4F6A-94BB-C97C0BA84D64}.Release|Any CPU.Build.0 = Release|Any CPU
{39EC839A-3C30-4922-A41E-6B09D1DDE5C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{39EC839A-3C30-4922-A41E-6B09D1DDE5C3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{39EC839A-3C30-4922-A41E-6B09D1DDE5C3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{39EC839A-3C30-4922-A41E-6B09D1DDE5C3}.Release|Any CPU.Build.0 = Release|Any CPU
{5EBACBFA-F11A-4BBF-8D02-91461F2293ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5EBACBFA-F11A-4BBF-8D02-91461F2293ED}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5EBACBFA-F11A-4BBF-8D02-91461F2293ED}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5EBACBFA-F11A-4BBF-8D02-91461F2293ED}.Release|Any CPU.Build.0 = Release|Any CPU
{903B2D7D-3B80-4BE9-8713-7447B704E1B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{903B2D7D-3B80-4BE9-8713-7447B704E1B0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{903B2D7D-3B80-4BE9-8713-7447B704E1B0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{903B2D7D-3B80-4BE9-8713-7447B704E1B0}.Release|Any CPU.Build.0 = Release|Any CPU
{3207D8E7-36E3-4714-9B03-7B5B3D6D351A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3207D8E7-36E3-4714-9B03-7B5B3D6D351A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3207D8E7-36E3-4714-9B03-7B5B3D6D351A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3207D8E7-36E3-4714-9B03-7B5B3D6D351A}.Release|Any CPU.Build.0 = Release|Any CPU
{7C3B0C7E-59FE-4E1A-A655-27AE119F9444}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7C3B0C7E-59FE-4E1A-A655-27AE119F9444}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7C3B0C7E-59FE-4E1A-A655-27AE119F9444}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7C3B0C7E-59FE-4E1A-A655-27AE119F9444}.Release|Any CPU.Build.0 = Release|Any CPU
{5EFAEF9B-90B9-4F05-9F70-F79AD77FFF86}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5EFAEF9B-90B9-4F05-9F70-F79AD77FFF86}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5EFAEF9B-90B9-4F05-9F70-F79AD77FFF86}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5EFAEF9B-90B9-4F05-9F70-F79AD77FFF86}.Release|Any CPU.Build.0 = Release|Any CPU
{4C86DF8F-A280-40D4-85B0-10B1BF66C15C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4C86DF8F-A280-40D4-85B0-10B1BF66C15C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4C86DF8F-A280-40D4-85B0-10B1BF66C15C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4C86DF8F-A280-40D4-85B0-10B1BF66C15C}.Release|Any CPU.Build.0 = Release|Any CPU
{81CAC44F-3711-4C8F-AE98-E02A7448782A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{81CAC44F-3711-4C8F-AE98-E02A7448782A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{81CAC44F-3711-4C8F-AE98-E02A7448782A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{81CAC44F-3711-4C8F-AE98-E02A7448782A}.Release|Any CPU.Build.0 = Release|Any CPU
{AF664D85-0A4B-4BAB-A2F8-83110C06553A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AF664D85-0A4B-4BAB-A2F8-83110C06553A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AF664D85-0A4B-4BAB-A2F8-83110C06553A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AF664D85-0A4B-4BAB-A2F8-83110C06553A}.Release|Any CPU.Build.0 = Release|Any CPU
{91B4DFC0-543C-43A7-A9E0-6817DCA277EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
@@ -117,7 +97,6 @@ Global
{991D0231-4623-496D-8BD0-9CA906029CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{991D0231-4623-496D-8BD0-9CA906029CBC}.Release|Any CPU.Build.0 = Release|Any CPU
{A8C3F298-76AB-4359-AB3C-986E313B4336}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A8C3F298-76AB-4359-AB3C-986E313B4336}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A8C3F298-76AB-4359-AB3C-986E313B4336}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A8C3F298-76AB-4359-AB3C-986E313B4336}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection

View File

@@ -44,11 +44,13 @@
<OptionInfer>On</OptionInfer>
</PropertyGroup>
<ItemGroup>
<Reference Include="DevExpress.Data.v21.2, Version=21.2.4.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a" />
<Reference Include="DevExpress.Data.Desktop.v21.2, Version=21.2.4.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a" />
<Reference Include="DevExpress.Utils.v21.2, Version=21.2.4.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a" />
<Reference Include="DevExpress.XtraEditors.v21.2, Version=21.2.4.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a" />
<Reference Include="DevExpress.XtraGrid.v21.2, Version=21.2.4.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a" />
<Reference Include="DevExpress.Drawing.v25.2, Version=25.2.3.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a" />
<Reference Include="DevExpress.Printing.v25.2.Core, Version=25.2.3.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a" />
<Reference Include="DevExpress.Data.v25.2, Version=25.2.3.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a" />
<Reference Include="DevExpress.Data.Desktop.v25.2, Version=25.2.3.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a" />
<Reference Include="DevExpress.Utils.v25.2, Version=25.2.3.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a" />
<Reference Include="DevExpress.XtraEditors.v25.2, Version=25.2.3.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a" />
<Reference Include="DevExpress.XtraGrid.v25.2, Version=25.2.3.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a" />
<Reference Include="DigitalData.Controls.LookupGrid, Version=3.3.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\DDMonorepo\Controls.LookupGrid\bin\Debug\DigitalData.Controls.LookupGrid.dll</HintPath>
@@ -155,4 +157,8 @@
<PropertyGroup>
<PostBuildEvent>powershell.exe -command "&amp; { &amp;'$(SolutionDir)copy-binary.ps1' '$(TargetPath)' '$(TargetFileName)' '$(ConfigurationName)' '$(ProjectName)' }"</PostBuildEvent>
</PropertyGroup>
<ItemGroup>
<Analyzer Include="D:\ProgramFiles\DevExpress 25.2\Components\Offline Packages\devexpress.data\25.2.3\analyzers\dotnet\DevExpress.Generator.dll" Visible="false" Condition="Exists('D:\ProgramFiles\DevExpress 25.2\Components\Offline Packages\devexpress.data\25.2.3\analyzers\dotnet\DevExpress.Generator.dll')" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,164 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{7C3B0C7E-59FE-4E1A-A655-27AE119F9444}</ProjectGuid>
<OutputType>Library</OutputType>
<RootNamespace>DigitalData.Modules.Patterns</RootNamespace>
<AssemblyName>DigitalData.Modules.Patterns</AssemblyName>
<FileAlignment>512</FileAlignment>
<MyType>Windows</MyType>
<TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<DefineDebug>true</DefineDebug>
<DefineTrace>true</DefineTrace>
<OutputPath>bin\Debug\</OutputPath>
<DocumentationFile>DigitalData.Modules.Patterns.xml</DocumentationFile>
<NoWarn>42016,41999,42017,42018,42019,42032,42036,42020,42021,42022</NoWarn>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<DefineDebug>false</DefineDebug>
<DefineTrace>true</DefineTrace>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DocumentationFile>DigitalData.Modules.Patterns.xml</DocumentationFile>
<NoWarn>42016,41999,42017,42018,42019,42032,42036,42020,42021,42022</NoWarn>
</PropertyGroup>
<PropertyGroup>
<OptionExplicit>On</OptionExplicit>
</PropertyGroup>
<PropertyGroup>
<OptionCompare>Binary</OptionCompare>
</PropertyGroup>
<PropertyGroup>
<OptionStrict>Off</OptionStrict>
</PropertyGroup>
<PropertyGroup>
<OptionInfer>On</OptionInfer>
</PropertyGroup>
<ItemGroup>
<Reference Include="DevExpress.Drawing.v26.1, Version=26.1.5.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a" />
<Reference Include="DevExpress.Printing.v26.1.Core, Version=26.1.5.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a" />
<Reference Include="DevExpress.Data.v26.1, Version=26.1.5.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a" />
<Reference Include="DevExpress.Data.Desktop.v26.1, Version=26.1.5.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a" />
<Reference Include="DevExpress.Utils.v26.1, Version=26.1.5.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a" />
<Reference Include="DevExpress.XtraEditors.v26.1, Version=26.1.5.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a" />
<Reference Include="DevExpress.XtraGrid.v26.1, Version=26.1.5.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a" />
<Reference Include="DigitalData.Controls.LookupGrid, Version=3.3.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\DDMonorepo\Controls.LookupGrid\bin\Debug\DigitalData.Controls.LookupGrid.dll</HintPath>
</Reference>
<Reference Include="DigitalData.Modules.Logging, Version=2.6.5.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\DigitalData.Modules.Logging.2.6.5\lib\net462\DigitalData.Modules.Logging.dll</HintPath>
</Reference>
<Reference Include="Interop.WINDREAMLib">
<HintPath>M:\Bibliotheken\3rdParty\windream\Interop.WINDREAMLib.dll</HintPath>
<EmbedInteropTypes>True</EmbedInteropTypes>
</Reference>
<Reference Include="Microsoft.CSharp" />
<Reference Include="NLog, Version=5.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\packages\NLog.5.0.5\lib\net46\NLog.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Configuration" />
<Reference Include="System.Data" />
<Reference Include="System.IO.Compression" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.ServiceModel" />
<Reference Include="System.Transactions" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Net.Http" />
</ItemGroup>
<ItemGroup>
<Import Include="Microsoft.VisualBasic" />
<Import Include="System" />
<Import Include="System.Collections" />
<Import Include="System.Collections.Generic" />
<Import Include="System.Data" />
<Import Include="System.Diagnostics" />
<Import Include="System.Linq" />
<Import Include="System.Xml.Linq" />
<Import Include="System.Threading.Tasks" />
</ItemGroup>
<ItemGroup>
<Compile Include="Constants.vb" />
<Compile Include="Modules\FileInformation.vb" />
<Compile Include="Modules\Globix\GlobixAutomatic.vb" />
<Compile Include="Modules\IDB.vb" />
<Compile Include="Modules\Globix\GlobixManual.vb" />
<Compile Include="Modules\Custom.vb" />
<Compile Include="Modules\Windream.vb" />
<Compile Include="Modules\User.vb" />
<Compile Include="IModule.vb" />
<Compile Include="Modules\Clipboard.vb" />
<Compile Include="Modules\Controls.vb" />
<Compile Include="Modules\Internal.vb" />
<Compile Include="Modules\BaseModule.vb" />
<Compile Include="Pattern.vb" />
<Compile Include="Patterns.vb" />
<Compile Include="My Project\AssemblyInfo.vb" />
<Compile Include="My Project\Application.Designer.vb">
<AutoGen>True</AutoGen>
<DependentUpon>Application.myapp</DependentUpon>
<DesignTime>True</DesignTime>
</Compile>
<Compile Include="My Project\Resources.Designer.vb">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="My Project\Settings.Designer.vb">
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
<Compile Include="Patterns2.vb" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="My Project\Resources.resx">
<Generator>VbMyResourcesResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.vb</LastGenOutput>
<CustomToolNamespace>My.Resources</CustomToolNamespace>
<SubType>Designer</SubType>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="My Project\Application.myapp">
<Generator>MyApplicationCodeGenerator</Generator>
<LastGenOutput>Application.Designer.vb</LastGenOutput>
</None>
<None Include="My Project\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<CustomToolNamespace>My</CustomToolNamespace>
<LastGenOutput>Settings.Designer.vb</LastGenOutput>
</None>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ZooFlow\ZooFlow.vbproj">
<Project>{81cac44f-3711-4c8f-ae98-e02a7448782a}</Project>
<Name>ZooFlow</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.VisualBasic.targets" />
<PropertyGroup>
<PostBuildEvent>powershell.exe -command "&amp; { &amp;'$(SolutionDir)copy-binary.ps1' '$(TargetPath)' '$(TargetFileName)' '$(ConfigurationName)' '$(ProjectName)' }"</PostBuildEvent>
</PropertyGroup>
<ItemGroup>
<Analyzer Include="D:\ProgramFiles\DevExpress 26.1\Components\Offline Packages\devexpress.data\26.1.5\analyzers\dotnet\DevExpress.Generator.dll" Visible="false" Condition="Exists('D:\ProgramFiles\DevExpress 26.1\Components\Offline Packages\devexpress.data\26.1.5\analyzers\dotnet\DevExpress.Generator.dll')" />
</ItemGroup>
</Project>