From 084fa29f3d2acc1947b17b25434c24626ccdee79 Mon Sep 17 00:00:00 2001 From: Jonathan Jenne Date: Fri, 9 Nov 2018 10:47:59 +0100 Subject: [PATCH] jj: Database version 0.0.3 --- EDMDesigner/FrmMain.vb | 2 +- Filesystem/Compression.vb | 54 ++++++ Filesystem/Encryption.vb | 105 +++++++++++ Filesystem/File.vb | 7 +- Filesystem/FileContainer.vb | 196 ++++++++++++++++++++ Filesystem/Filesystem.vbproj | 3 + LookupGrid/LookupControl.Designer.vb | 7 +- Modules.Database/Firebird.vb | 119 ++++++++---- Modules.Database/My Project/AssemblyInfo.vb | 2 +- Modules.Logging.Test/ConstructorUnitTest.vb | 10 +- TestGUI/Form1.Designer.vb | 16 +- 11 files changed, 467 insertions(+), 54 deletions(-) create mode 100644 Filesystem/Compression.vb create mode 100644 Filesystem/Encryption.vb create mode 100644 Filesystem/FileContainer.vb diff --git a/EDMDesigner/FrmMain.vb b/EDMDesigner/FrmMain.vb index a40f81e9..00e4755c 100644 --- a/EDMDesigner/FrmMain.vb +++ b/EDMDesigner/FrmMain.vb @@ -57,7 +57,7 @@ Public Class FrmMain Private Sub Init() _firebird = New Firebird(_logConfig, My.Settings.fbDatasource, My.Settings.fbDatabaseLocation, My.Settings.fbUser, My.Settings.fbPassword) - + If _firebird.ConnectionFailed Then MsgBox("Database connection failed. Please check the log.", vbCritical) Exit Sub diff --git a/Filesystem/Compression.vb b/Filesystem/Compression.vb new file mode 100644 index 00000000..19470f70 --- /dev/null +++ b/Filesystem/Compression.vb @@ -0,0 +1,54 @@ +Imports System.IO +Imports System.IO.Compression + +Friend Class Compression + Public Async Function CompressAsync(data As Byte()) As Task(Of Byte()) + Return Await Task.Run(Function() As Byte() + Return Compress(data) + End Function) + End Function + + Public Function Compress(data As Byte()) As Byte() + ' ByteArray in Stream umwandeln + Using originalStream As New MemoryStream(data) + ' Ziel Stream erstellen + Using compressedStream As New MemoryStream() + ' Gzip-Stream erstellen, der alle Daten komprimiert und zu compressedStream durchleitet + ' + ' > MemoryStream > GzipStream > MemoryStream + ' originalStream --> compressionStream --> compressedFileStream + ' + Using compressionStream As New GZipStream(compressedStream, CompressionMode.Compress) + originalStream.CopyTo(compressionStream) + compressionStream.Close() + Return compressedStream.ToArray() + End Using + End Using + End Using + End Function + + Public Async Function DecompressAsync(data As Byte()) As Task(Of Byte()) + Return Await Task.Run(Function() As Byte() + Return Decompress(data) + End Function) + End Function + + Public Function Decompress(data As Byte()) As Byte() + ' ByteArray in Stream umwandeln + Using compressedStream As New MemoryStream(data) + ' Ziel Stream erstellen + Using decompressedStream As New MemoryStream() + ' Gzip-Stream erstellen, der alle Daten komprimiert und zu compressedStream durchleitet + ' + ' > MemoryStream > GzipStream > MemoryStream + ' compressedStream --> decompressionStream --> decompressedStream + ' + Using decompressionStream As New GZipStream(compressedStream, CompressionMode.Decompress) + decompressionStream.CopyTo(decompressedStream) + Return decompressedStream.ToArray() + End Using + End Using + End Using + End Function + +End Class diff --git a/Filesystem/Encryption.vb b/Filesystem/Encryption.vb new file mode 100644 index 00000000..cdb2cd31 --- /dev/null +++ b/Filesystem/Encryption.vb @@ -0,0 +1,105 @@ +Imports System.IO +Imports System.Security.Cryptography + +''' +''' https://stackoverflow.com/questions/10168240/encrypting-decrypting-a-string-in-c-sharp +''' +Friend Class Encryption + ' This constant is used to determine the keysize of the encryption algorithm in bits. + ' We divide this by 8 within the code below to get the equivalent number of bytes. + Private Const Keysize As Integer = 256 + ' This constant determines the number of iterations for the password bytes generation function. + Private Const DerivationIterations As Integer = 1000 + Private ReadOnly password As String + + Public Sub New(password As String) + If IsNothing(password) Then + Throw New ArgumentNullException("password") + End If + + Me.password = password + End Sub + + Public Async Function EncryptAsync(plainTextBytes As Byte()) As Task(Of Byte()) + Return Await Task.Run(Function() As Byte() + Return Encrypt(plainTextBytes) + End Function) + End Function + + Public Function Encrypt(plainTextBytes As Byte()) As Byte() + ' Salt and IV is randomly generated each time, but is preprended to encrypted cipher text + ' so that the same Salt and IV values can be used when decrypting. + Dim saltStringBytes = Generate256BitsOfRandomEntropy() + Dim ivStringBytes = Generate256BitsOfRandomEntropy() + Using password = New Rfc2898DeriveBytes(Me.password, saltStringBytes, DerivationIterations) + Dim keyBytes = password.GetBytes(Keysize / 8) + Using symmetricKey = New RijndaelManaged() + symmetricKey.BlockSize = 256 + symmetricKey.Mode = CipherMode.CBC + symmetricKey.Padding = PaddingMode.PKCS7 + Using encryptor = symmetricKey.CreateEncryptor(keyBytes, ivStringBytes) + Using memoryStream = New MemoryStream() + Using cryptoStream = New CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write) + cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length) + cryptoStream.FlushFinalBlock() + ' Create the final bytes as a concatenation of the random salt bytes, the random iv bytes and the cipher bytes. + Dim cipherTextBytes = saltStringBytes + cipherTextBytes = cipherTextBytes.Concat(ivStringBytes).ToArray() + cipherTextBytes = cipherTextBytes.Concat(memoryStream.ToArray()).ToArray() + memoryStream.Close() + cryptoStream.Close() + Return cipherTextBytes + End Using + End Using + End Using + End Using + End Using + End Function + + Public Async Function DecryptAsync(cipherTextBytesWithSaltAndIv As Byte()) As Task(Of Byte()) + Return Await Task.Run(Function() As Byte() + Return Decrypt(cipherTextBytesWithSaltAndIv) + End Function) + End Function + + Public Function Decrypt(cipherTextBytesWithSaltAndIv As Byte()) As Byte() + ' Get the complete stream of bytes that represent: + ' [32 bytes of Salt] + [32 bytes of IV] + [n bytes of CipherText] + ' Get the saltbytes by extracting the first 32 bytes from the supplied cipherText bytes. + Dim saltStringBytes = cipherTextBytesWithSaltAndIv.Take(Keysize / 8).ToArray() + ' Get the IV bytes by extracting the next 32 bytes from the supplied cipherText bytes. + Dim ivStringBytes = cipherTextBytesWithSaltAndIv.Skip(Keysize / 8).Take(Keysize / 8).ToArray() + ' Get the actual cipher text bytes by removing the first 64 bytes from the cipherText string. + Dim cipherTextBytes = cipherTextBytesWithSaltAndIv.Skip((Keysize / 8) * 2).Take(cipherTextBytesWithSaltAndIv.Length - ((Keysize / 8) * 2)).ToArray() + + Using password = New Rfc2898DeriveBytes(Me.password, saltStringBytes, DerivationIterations) + Dim keyBytes = password.GetBytes(Keysize / 8) + Using symmetricKey = New RijndaelManaged() + symmetricKey.BlockSize = 256 + symmetricKey.Mode = CipherMode.CBC + symmetricKey.Padding = PaddingMode.PKCS7 + Using decryptor = symmetricKey.CreateDecryptor(keyBytes, ivStringBytes) + Using memoryStream = New MemoryStream(cipherTextBytes) + Using cryptoStream = New CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read) + Dim plainTextBytes = New Byte(cipherTextBytes.Length - 1) {} + Dim decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length) + memoryStream.Close() + cryptoStream.Close() + Return plainTextBytes + End Using + End Using + End Using + End Using + End Using + End Function + + Private Shared Function Generate256BitsOfRandomEntropy() As Byte() + Dim randomBytes = New Byte(31) {} + ' 32 Bytes will give us 256 bits. + Using rngCsp = New RNGCryptoServiceProvider() + ' Fill the array with cryptographically secure random bytes. + rngCsp.GetBytes(randomBytes) + End Using + Return randomBytes + End Function +End Class diff --git a/Filesystem/File.vb b/Filesystem/File.vb index 38ba28d6..31b4d400 100644 --- a/Filesystem/File.vb +++ b/Filesystem/File.vb @@ -30,16 +30,20 @@ Public Class File End Sub ''' - ''' Removes files in a directory filtered by filename, extension and last write date + ''' Removes files in a directory filtered by filename, extension and last write date. ''' ''' The directory in which files will be deleted ''' Only delete files which are older than x days. Must be between 0 and 1000 days. ''' A filename filter which will be checked ''' A file extension which will be checked ''' Should the function continue with deleting when a file could not be deleted? + ''' When Path is not a directory + ''' When Path was not found + ''' When FileKeepTime is below 0 or above 1000 ''' True if all files were deleted or if no files were deleted, otherwise false Public Function RemoveFiles(Path As String, FileKeepTime As Integer, FileBaseName As String, Optional FileExtension As String = "log", Optional ContinueOnError As Boolean = True) As Boolean If Not TestPathIsDirectory(Path) Then + Throw New ArgumentException($"Path {Path} is not a directory!") End If @@ -93,5 +97,4 @@ Public Class File Dim oIsDirectory As Boolean = (System.IO.File.GetAttributes(Path) And FileAttributes.Directory) = FileAttributes.Directory Return oIsDirectory End Function - End Class diff --git a/Filesystem/FileContainer.vb b/Filesystem/FileContainer.vb new file mode 100644 index 00000000..cb4fcb20 --- /dev/null +++ b/Filesystem/FileContainer.vb @@ -0,0 +1,196 @@ +Imports System.IO +Imports System.Runtime.Serialization.Formatters.Binary + +Public Class FileContainer + Private _files As List(Of FileEntry) + Private _crypto As Encryption + Private _compression As Compression + Private _formatter As BinaryFormatter + + + Public Class FileEntry + Public FileId As String + Public Contents As Byte() + End Class + + ''' + ''' Gibt eine Auflistung der Dateien in Container zurück. + ''' + ''' Eine Liste von Objekten der Klasse FileContainer.FileEntry + Public ReadOnly Property Files As List(Of FileEntry) + Get + Return _files + End Get + End Property + + ''' + ''' Erstellt eine Representation eines Datei Containers, der mit dem angegebenen Passwort geschützt ist. + ''' Ist für das speichern von neuen Containern und für das laden vorhandener Container nötig. + ''' + ''' Das Passwort, mit dem der Container ver- und entschlüsselt wird + ''' + ''' Dim password = "meinpasswort" + ''' Dim container = new FileContainer(password) + ''' + Public Sub New(password As String) + _crypto = New Encryption(password) + _compression = New Compression() + _formatter = New BinaryFormatter() + + _files = New List(Of FileEntry) + End Sub + + ''' + ''' Speichert einen Datei Container am angegebenen Pfad. + ''' Davor werden die Dateien in Files komprimiert und dann verschlüsselt. + ''' + ''' Der Pfad mit Dateiname, unter dem der Container abgelegt wird. + ''' + ''' + ''' container.AddFile("datei1.txt") + ''' container.AddFile("datei2.pdf") + ''' container.Save("container.enc") + ''' + Public Sub Save(path As String) + ' 1. Serialize + Dim bFiles As Byte() = Serialize(_files) + ' 2. Compress + Dim bFilesCompressed As Byte() = _compression.Compress(bFiles) + ' 3. Encrypt + Dim bFilesEncrypted As Byte() = _crypto.Encrypt(bFilesCompressed) + + BytesToFile(bFilesEncrypted, path) + End Sub + + ''' + ''' Speichert einen Datei Container am angegebenen Pfad. + ''' Davor werden die Dateien in Files komprimiert und dann verschlüsselt. + ''' + ''' Der Pfad mit Dateiname, unter dem der Container abgelegt wird. + ''' + ''' Public Async Sub foobar() + ''' ... + ''' container.AddFile("datei1.txt") + ''' container.AddFile("datei2.pdf") + ''' await container.SaveAsync("container.enc") + ''' ... + ''' End Sub + ''' + Public Async Function SaveAsync(path As String) As Task + ' 1. Serialize + Dim bFiles As Byte() = Serialize(_files) + ' 2. Compress + Dim bFilesCompressed As Byte() = Await _compression.CompressAsync(bFiles) + 'Dim bFilesCompressed = bFiles + ' 3. Encrypt + Dim bFilesEncrypted As Byte() = Await _crypto.EncryptAsync(bFilesCompressed) + + BytesToFileAsync(bFilesEncrypted, path) + End Function + + ''' + ''' Lädt einen Datei Container aus dem angegebenen Pfad. + ''' Anschließend werden die enthaltenen Dateien entschlüsselt, dekomprimiert und in Files abgelegt. + ''' + ''' Der Pfad mit Dateiname, von dem der Container geladen wird. + ''' + ''' + ''' container.Load("container.enc") + ''' Dim files As List(Of FileContainer.FileEntry) = container.Files + ''' + Public Sub Load(path As String) + Dim bFilesEncrypted As Byte() = FileToBytes(path) + + ' 1. Decrypt + Dim bFiles As Byte() = _crypto.Decrypt(bFilesEncrypted) + ' 2. Decompress + Dim bFilesDecompressed As Byte() = _compression.Decompress(bFiles) + ' 3. Deserialize + _files = Deserialize(bFilesDecompressed) + End Sub + + ''' + ''' Lädt einen Datei Container aus dem angegebenen Pfad. + ''' Anschließend werden die enthaltenen Dateien entschlüsselt, dekomprimiert und in Files abgelegt. + ''' + ''' Der Pfad mit Dateiname, von dem der Container geladen wird. + ''' + ''' Public Async Sub foobar() + ''' ... + ''' await container.LoadAsync("container.enc") + ''' Dim files As List(Of FileContainer.FileEntry) = container.Files + ''' ... + ''' End Sub + ''' + Public Async Function LoadAsync(path As String) As Task + Dim bFilesEncrypted As Byte() = Await FileToBytesAsync(path) + + ' 1. Decrypt + Dim bFiles As Byte() = Await _crypto.DecryptAsync(bFilesEncrypted) + ' 2. Decompress + Dim bFilesDecompressed As Byte() = Await _compression.DecompressAsync(bFiles) + 'Dim bFilesDecompressed = bFiles + ' 3. Deserialize + _files = Deserialize(bFilesDecompressed) + End Function + + ''' + ''' Fügt einem Datei Container eine neue Datei hinzu + ''' + ''' Der Pfad zur Datei, die im Container gespeichert werden soll + ''' + Public Sub AddFile(filePath As String) + Dim fi As New FileInfo(filePath) + Dim fe As New FileEntry() + + If fi.Exists = False Then + Throw New FileNotFoundException($"{filePath} is not a valid path.") + End If + + fe.Contents = IO.File.ReadAllBytes(filePath) + fe.FileId = Guid.NewGuid().ToString() + + _files.Add(fe) + End Sub + + Public Function GetFile(fileId As String) As FileEntry + Return _files.Where(Function(f) f.FileId = fileId).FirstOrDefault() + End Function + + Private Function Serialize(list As List(Of FileEntry)) As Byte() + Using ms As New MemoryStream + _formatter.Serialize(ms, list) + Return ms.ToArray() + End Using + End Function + + Private Function Deserialize(data As Byte()) As List(Of FileEntry) + Using ms As New MemoryStream(data) + Dim list = TryCast(_formatter.Deserialize(ms), List(Of FileEntry)) + Return list + End Using + End Function + + Private Sub BytesToFile(data As Byte(), filePath As String) + IO.File.WriteAllBytes(filePath, data) + End Sub + + Private Function FileToBytes(filePath As String) As Byte() + Return IO.File.ReadAllBytes(filePath) + End Function + + Private Async Function FileToBytesAsync(filePath As String) As Task(Of Byte()) + Using file = New FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, True) + Dim buff As Byte() = New Byte(file.Length - 1) {} + Await file.ReadAsync(buff, 0, file.Length) + file.Close() + Return buff + End Using + End Function + + Private Async Sub BytesToFileAsync(data As Byte(), filePath As String) + Using sourceStream As New FileStream(filePath, FileMode.Append, FileAccess.Write, FileShare.None, bufferSize:=4096, useAsync:=True) + Await sourceStream.WriteAsync(data, 0, data.Length) + End Using + End Sub +End Class diff --git a/Filesystem/Filesystem.vbproj b/Filesystem/Filesystem.vbproj index 48eaf125..75bfd148 100644 --- a/Filesystem/Filesystem.vbproj +++ b/Filesystem/Filesystem.vbproj @@ -72,7 +72,10 @@ + + + True diff --git a/LookupGrid/LookupControl.Designer.vb b/LookupGrid/LookupControl.Designer.vb index a0f71ae1..3c55c38f 100644 --- a/LookupGrid/LookupControl.Designer.vb +++ b/LookupGrid/LookupControl.Designer.vb @@ -24,14 +24,15 @@ Partial Class LookupControl Private Sub InitializeComponent() Me.SuspendLayout() ' - 'Grid + 'LookupControl ' Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!) Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font Me.AutoSize = True + Me.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink Me.BackColor = System.Drawing.Color.Transparent - Me.Name = "Grid" - Me.Size = New System.Drawing.Size(270, 35) + Me.Name = "LookupControl" + Me.Size = New System.Drawing.Size(0, 0) Me.ResumeLayout(False) End Sub diff --git a/Modules.Database/Firebird.vb b/Modules.Database/Firebird.vb index c2a9b95c..59ef39de 100644 --- a/Modules.Database/Firebird.vb +++ b/Modules.Database/Firebird.vb @@ -5,9 +5,9 @@ Imports DigitalData.Modules.Logging ''' ''' MODULE: Firebird ''' -''' VERSION: 0.0.0.2 +''' VERSION: 0.0.0.3 ''' -''' DATE: 03.09.2018 +''' DATE: 08.11.2018 ''' ''' DESCRIPTION: ''' @@ -17,15 +17,15 @@ Imports DigitalData.Modules.Logging ''' ''' FirebirdSql.Data.FirebirdClient, >= 6.0.0 ''' -''' PARAMETERS: LogFactory, NLog.LogFactory +''' PARAMETERS: LogConfig, DigitalData.Modules.Logging.LogConfig ''' The LogFactory containing the current log config. Used to instanciate the class logger for this and any dependent class ''' ''' DataSource, String -''' The location of the Database in the format `127.0.0.1:E:\Path\To\Database.FDB` -''' -''' Database, String ''' The server where the database lives, for example 127.0.0.1 or dd-vmx09-vm03 ''' +''' Database, String +''' The location of the Database in the format `127.0.0.1:E:\Path\To\Database.FDB` +''' ''' User, String ''' The user name to connect as ''' @@ -52,23 +52,26 @@ Public Class Firebird Private _connectionDatabase As String Private _connectionUsername As String Private _connectionPassword As String + Private _connectionString As String - Public ReadOnly Property ConnectionEstablished As Boolean - Public ReadOnly Property ConnectionFailed As Boolean Public ReadOnly Property ConnectionString As String + Get + Return _connectionString + End Get + End Property + Public ReadOnly Property DatabaseName As String Get Dim oRegex As New Regex("^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:") Dim oPath As String = oRegex.Replace(_connectionDatabase, "") - Dim fi As New IO.FileInfo(oPath) - Return fi.Name + Dim oFileInfo As New IO.FileInfo(oPath) + Return oFileInfo.Name End Get End Property ''' ''' ''' - ''' ''' ''' ''' @@ -84,7 +87,7 @@ Public Class Firebird _connectionDatabase = Database _connectionUsername = User _connectionPassword = Password - _ConnectionString = oConnectionString + _connectionString = oConnectionString ' Test the connection Dim oConnection = GetConnection() @@ -98,15 +101,11 @@ Public Class Firebird Public Function GetConnection() As FbConnection Try - Dim oConnection = New FbConnection(_ConnectionString) + Dim oConnection = New FbConnection(_connectionString) oConnection.Open() - _ConnectionEstablished = True - _ConnectionFailed = False Return oConnection Catch ex As Exception - _ConnectionFailed = True - _ConnectionEstablished = False _logger.Error(ex) Return Nothing @@ -134,16 +133,18 @@ Public Class Firebird ''' Executes a non-query command. ''' ''' The command to execute + ''' The Firebird connection to use ''' True, if command was executed sucessfully. Otherwise false. - Public Function ExecuteNonQuery(SqlCommand As String) As Boolean - Try - Dim oConnection As FbConnection = GetConnection() + Public Function ExecuteNonQueryWithConnection(SqlCommand As String, Connection As FbConnection) As Boolean + Dim oConnection As FbConnection = GetConnection() + Dim oTransaction As FbTransaction - If oConnection Is Nothing Then - Return False - End If + If oConnection Is Nothing Then + Return False + End If - Dim oTransaction As FbTransaction = oConnection.BeginTransaction() + Try + oTransaction = oConnection.BeginTransaction() Dim oCommand As New FbCommand With { .CommandText = SqlCommand, .Connection = oConnection, @@ -160,29 +161,41 @@ Public Class Firebird End Try End Function + ''' + ''' Executes a non-query command. + ''' + ''' The command to execute + ''' True, if command was executed sucessfully. Otherwise false. + Public Function ExecuteNonQuery(SqlCommand As String) As Boolean + Dim oConnection As FbConnection = GetConnection() + Dim oScalarValue As Object = ExecuteNonQueryWithConnection(SqlCommand, oConnection) + + oConnection.Close() + + Return oScalarValue + End Function + ''' ''' Executes a sql query resulting in a scalar value. ''' ''' The query to execute + ''' The Firebird connection to use ''' The scalar value if the command was executed successfully. Nothing otherwise. - Public Function GetScalarValue(SqlQuery As String) As Object + Public Function GetScalarValueWithConnection(SqlQuery As String, Connection As FbConnection) As Object Try - Dim oConnection As FbConnection = GetConnection() - - If oConnection Is Nothing Then + If Connection Is Nothing Then Return Nothing End If - Dim oTransaction As FbTransaction = oConnection.BeginTransaction() + Dim oTransaction As FbTransaction = Connection.BeginTransaction() Dim oCommand As New FbCommand With { .CommandText = SqlQuery, - .Connection = oConnection, + .Connection = Connection, .Transaction = oTransaction } Dim oResult As Object = oCommand.ExecuteScalar() oTransaction.Commit() - oConnection.Close() Return oResult Catch ex As Exception @@ -191,33 +204,59 @@ Public Class Firebird End Try End Function + ''' + ''' Executes a sql query resulting in a scalar value. + ''' + ''' The query to execute + ''' The scalar value if the command was executed successfully. Nothing otherwise. + Public Function GetScalarValue(SqlQuery As String) As Object + Dim oConnection As FbConnection = GetConnection() + Dim oScalarValue As Object = GetScalarValueWithConnection(SqlQuery, oConnection) + + oConnection.Close() + + Return oScalarValue + End Function + ''' ''' Executes a sql query resulting in a table of values. ''' ''' The query to execute + ''' The Firebird connection to use ''' A datatable containing the results if the command was executed successfully. Nothing otherwise. - Public Function GetDatatable(SqlQuery As String) As DataTable + Public Function GetDatatableWithConnection(SqlQuery As String, Connection As FbConnection) As DataTable Try - Dim oConnection As FbConnection = GetConnection() - - If oConnection Is Nothing Then + If Connection Is Nothing Then Return Nothing End If Dim oCommand As New FbCommand With { .CommandText = SqlQuery, - .Connection = oConnection + .Connection = Connection } Dim oAdapter As New FbDataAdapter(oCommand) - Dim oDatatable As New DataTable() + Dim oDatatable As New DataTable() With {.TableName = "DDRESULT"} oAdapter.Fill(oDatatable) - oConnection.Close() Return oDatatable Catch ex As Exception - _logger.Error(ex, $"Error in ReturnDatatable while executing command: '{SqlQuery}'") - Return Nothing + _logger.Error(ex, $"Error in GetDatatableWithConnection while executing command: '{SqlQuery}'") + Throw ex End Try End Function + + ''' + ''' Executes a sql query resulting in a table of values. + ''' + ''' The query to execute + ''' A datatable containing the results if the command was executed successfully. Nothing otherwise. + Public Function GetDatatable(SqlQuery As String) As DataTable + Dim oConnection As FbConnection = GetConnection() + Dim oDatatable As DataTable = GetDatatableWithConnection(SqlQuery, oConnection) + + oConnection.Close() + + Return oDatatable + End Function End Class diff --git a/Modules.Database/My Project/AssemblyInfo.vb b/Modules.Database/My Project/AssemblyInfo.vb index a9b1823b..2c942633 100644 --- a/Modules.Database/My Project/AssemblyInfo.vb +++ b/Modules.Database/My Project/AssemblyInfo.vb @@ -31,5 +31,5 @@ Imports System.Runtime.InteropServices ' übernehmen, indem Sie "*" eingeben: ' - + diff --git a/Modules.Logging.Test/ConstructorUnitTest.vb b/Modules.Logging.Test/ConstructorUnitTest.vb index f8b7bbe7..96aea5f2 100644 --- a/Modules.Logging.Test/ConstructorUnitTest.vb +++ b/Modules.Logging.Test/ConstructorUnitTest.vb @@ -7,11 +7,11 @@ Imports DigitalData.Modules.Logging ''' Test creating the log path in various configurations ''' Public Class ConstructorUnitTest - Private Shared _currentDirectoryLogPath = Path.Combine(Directory.GetCurrentDirectory(), "Log") - Private Shared _appdataDirectoryLogPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Digital Data", "Modules.Logging") - Private Shared _customDirectoryLogPath = Path.Combine(Directory.GetCurrentDirectory(), "CustomLogFolder") - Private Shared _restrictedDirectoryLogPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), "ShouldNotBeCreated") - Private Shared _nonsenseDirectoryLogPath = "X:\FOO\BAR\BAZ\QUUX" + Private Shared ReadOnly _currentDirectoryLogPath = Path.Combine(Directory.GetCurrentDirectory(), "Log") + Private Shared ReadOnly _appdataDirectoryLogPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Digital Data", "Modules.Logging") + Private Shared ReadOnly _customDirectoryLogPath = Path.Combine(Directory.GetCurrentDirectory(), "CustomLogFolder") + Private Shared ReadOnly _restrictedDirectoryLogPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), "ShouldNotBeCreated") + Private Shared ReadOnly _nonsenseDirectoryLogPath = "X:\FOO\BAR\BAZ\QUUX" Public Sub TestConstructorCurrentDirectory() Dim oLogConfig As New LogConfig(LogConfig.PathType.CurrentDirectory) diff --git a/TestGUI/Form1.Designer.vb b/TestGUI/Form1.Designer.vb index ece45d55..85fef004 100644 --- a/TestGUI/Form1.Designer.vb +++ b/TestGUI/Form1.Designer.vb @@ -45,6 +45,7 @@ Partial Class Form1 Me.Label6 = New System.Windows.Forms.Label() Me.Button4 = New System.Windows.Forms.Button() Me.LookupControl1 = New DigitalData.Controls.LookupGrid.LookupControl() + Me.Button6 = New System.Windows.Forms.Button() CType(Me.GridControl1, System.ComponentModel.ISupportInitialize).BeginInit() CType(Me.GridView1, System.ComponentModel.ISupportInitialize).BeginInit() Me.SuspendLayout() @@ -235,19 +236,29 @@ Partial Class Form1 Me.LookupControl1.AutoSize = True Me.LookupControl1.BackColor = System.Drawing.Color.Transparent Me.LookupControl1.DataSource = Nothing - Me.LookupControl1.Location = New System.Drawing.Point(695, 214) + Me.LookupControl1.Location = New System.Drawing.Point(684, 195) Me.LookupControl1.MultiSelect = True Me.LookupControl1.Name = "LookupControl1" Me.LookupControl1.SelectedValues = CType(resources.GetObject("LookupControl1.SelectedValues"), System.Collections.Generic.List(Of String)) - Me.LookupControl1.Size = New System.Drawing.Size(270, 23) + Me.LookupControl1.Size = New System.Drawing.Size(270, 87) Me.LookupControl1.TabIndex = 23 ' + 'Button6 + ' + Me.Button6.Location = New System.Drawing.Point(637, 240) + Me.Button6.Name = "Button6" + Me.Button6.Size = New System.Drawing.Size(75, 23) + Me.Button6.TabIndex = 24 + Me.Button6.Text = "Button6" + Me.Button6.UseVisualStyleBackColor = True + ' 'Form1 ' Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!) Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font Me.ClientSize = New System.Drawing.Size(1050, 487) Me.Controls.Add(Me.LookupControl1) + Me.Controls.Add(Me.Button6) Me.Controls.Add(Me.Button4) Me.Controls.Add(Me.Label6) Me.Controls.Add(Me.TextBox1) @@ -300,4 +311,5 @@ Partial Class Form1 Friend WithEvents Label6 As Label Friend WithEvents Button4 As Button Friend WithEvents LookupControl1 As DigitalData.Controls.LookupGrid.LookupControl + Friend WithEvents Button6 As Button End Class