From 1f0e6516fdaf9c90771c56be348c9184f22e861f Mon Sep 17 00:00:00 2001 From: Andrei Tudor Date: Tue, 7 Nov 2023 10:13:22 +0200 Subject: [PATCH] New SqlDatabase functions --- PluginManager/Bot/Boot.cs | 2 +- PluginManager/Config.cs | 4 +- PluginManager/Database/SqlDatabase.cs | 251 +++++++++++++++++++++----- PluginManager/Loaders/Loader.cs | 14 +- 4 files changed, 215 insertions(+), 56 deletions(-) diff --git a/PluginManager/Bot/Boot.cs b/PluginManager/Bot/Boot.cs index 849cb98..713693a 100644 --- a/PluginManager/Bot/Boot.cs +++ b/PluginManager/Bot/Boot.cs @@ -85,7 +85,7 @@ public class Boot await commandServiceHandler.InstallCommandsAsync(); - Config._DiscordBotClient = this; + Config.DiscordBotClient = this; while (!isReady) ; } diff --git a/PluginManager/Config.cs b/PluginManager/Config.cs index 021966b..b0dde70 100644 --- a/PluginManager/Config.cs +++ b/PluginManager/Config.cs @@ -13,9 +13,9 @@ public class Config public static Logger Logger; public static SettingsDictionary AppSettings; - internal static Boot? _DiscordBotClient; + internal static Boot? DiscordBotClient; - public static Boot? DiscordBot => _DiscordBotClient; + public static Boot? DiscordBot => DiscordBotClient; public static async Task Initialize() { diff --git a/PluginManager/Database/SqlDatabase.cs b/PluginManager/Database/SqlDatabase.cs index 43289a4..6b65a84 100644 --- a/PluginManager/Database/SqlDatabase.cs +++ b/PluginManager/Database/SqlDatabase.cs @@ -9,7 +9,7 @@ namespace PluginManager.Database; public class SqlDatabase { - private readonly SQLiteConnection Connection; + private readonly SQLiteConnection _connection; /// /// Initialize a SQL connection by specifing its private path @@ -22,7 +22,7 @@ public class SqlDatabase if (!File.Exists(fileName)) SQLiteConnection.CreateFile(fileName); var connectionString = $"URI=file:{fileName}"; - Connection = new SQLiteConnection(connectionString); + _connection = new SQLiteConnection(connectionString); } @@ -32,7 +32,7 @@ public class SqlDatabase /// public async Task Open() { - await Connection.OpenAsync(); + await _connection.OpenAsync(); } /// @@ -55,7 +55,7 @@ public class SqlDatabase query += ")"; - var command = new SQLiteCommand(query, Connection); + var command = new SQLiteCommand(query, _connection); await command.ExecuteNonQueryAsync(); } @@ -79,7 +79,7 @@ public class SqlDatabase query += ")"; - var command = new SQLiteCommand(query, Connection); + var command = new SQLiteCommand(query, _connection); command.ExecuteNonQuery(); } @@ -94,7 +94,7 @@ public class SqlDatabase { var query = $"DELETE FROM {tableName} WHERE {KeyName} = '{KeyValue}'"; - var command = new SQLiteCommand(query, Connection); + var command = new SQLiteCommand(query, _connection); await command.ExecuteNonQueryAsync(); } @@ -109,7 +109,7 @@ public class SqlDatabase { var query = $"DELETE FROM {tableName} WHERE {KeyName} = '{KeyValue}'"; - var command = new SQLiteCommand(query, Connection); + var command = new SQLiteCommand(query, _connection); command.ExecuteNonQuery(); } @@ -163,7 +163,7 @@ public class SqlDatabase throw new Exception($"Table {tableName} does not exist"); await ExecuteAsync( - $"UPDATE {tableName} SET {ResultColumnName}='{ResultColumnValue}' WHERE {keyName}='{KeyValue}'"); + $"UPDATE {tableName} SET {ResultColumnName}='{ResultColumnValue}' WHERE {keyName}='{KeyValue}'"); } /// @@ -224,7 +224,7 @@ public class SqlDatabase /// public async void Stop() { - await Connection.CloseAsync(); + await _connection.CloseAsync(); } /// @@ -236,9 +236,9 @@ public class SqlDatabase /// public async Task AddColumnsToTableAsync(string tableName, string[] columns, string TYPE = "TEXT") { - var command = Connection.CreateCommand(); + var command = _connection.CreateCommand(); command.CommandText = $"SELECT * FROM {tableName}"; - var reader = await command.ExecuteReaderAsync(); + var reader = await command.ExecuteReaderAsync(); var tableColumns = new List(); for (var i = 0; i < reader.FieldCount; i++) tableColumns.Add(reader.GetName(i)); @@ -260,9 +260,9 @@ public class SqlDatabase /// public void AddColumnsToTable(string tableName, string[] columns, string TYPE = "TEXT") { - var command = Connection.CreateCommand(); + var command = _connection.CreateCommand(); command.CommandText = $"SELECT * FROM {tableName}"; - var reader = command.ExecuteReader(); + var reader = command.ExecuteReader(); var tableColumns = new List(); for (var i = 0; i < reader.FieldCount; i++) tableColumns.Add(reader.GetName(i)); @@ -282,7 +282,7 @@ public class SqlDatabase /// True if the table exists, false if not public async Task TableExistsAsync(string tableName) { - var cmd = Connection.CreateCommand(); + var cmd = _connection.CreateCommand(); cmd.CommandText = $"SELECT name FROM sqlite_master WHERE type='table' AND name='{tableName}'"; var result = await cmd.ExecuteScalarAsync(); @@ -298,7 +298,7 @@ public class SqlDatabase /// True if the table exists, false if not public bool TableExists(string tableName) { - var cmd = Connection.CreateCommand(); + var cmd = _connection.CreateCommand(); cmd.CommandText = $"SELECT name FROM sqlite_master WHERE type='table' AND name='{tableName}'"; var result = cmd.ExecuteScalar(); @@ -315,7 +315,7 @@ public class SqlDatabase /// public async Task CreateTableAsync(string tableName, params string[] columns) { - var cmd = Connection.CreateCommand(); + var cmd = _connection.CreateCommand(); cmd.CommandText = $"CREATE TABLE IF NOT EXISTS {tableName} ({string.Join(", ", columns)})"; await cmd.ExecuteNonQueryAsync(); } @@ -328,7 +328,7 @@ public class SqlDatabase /// public void CreateTable(string tableName, params string[] columns) { - var cmd = Connection.CreateCommand(); + var cmd = _connection.CreateCommand(); cmd.CommandText = $"CREATE TABLE IF NOT EXISTS {tableName} ({string.Join(", ", columns)})"; cmd.ExecuteNonQuery(); } @@ -340,10 +340,10 @@ public class SqlDatabase /// The number of rows that the query modified public async Task ExecuteAsync(string query) { - if (!Connection.State.HasFlag(ConnectionState.Open)) - await Connection.OpenAsync(); - var command = new SQLiteCommand(query, Connection); - var answer = await command.ExecuteNonQueryAsync(); + if (!_connection.State.HasFlag(ConnectionState.Open)) + await _connection.OpenAsync(); + var command = new SQLiteCommand(query, _connection); + var answer = await command.ExecuteNonQueryAsync(); return answer; } @@ -354,10 +354,10 @@ public class SqlDatabase /// The number of rows that the query modified public int Execute(string query) { - if (!Connection.State.HasFlag(ConnectionState.Open)) - Connection.Open(); - var command = new SQLiteCommand(query, Connection); - var r = command.ExecuteNonQuery(); + if (!_connection.State.HasFlag(ConnectionState.Open)) + _connection.Open(); + var command = new SQLiteCommand(query, _connection); + var r = command.ExecuteNonQuery(); return r; } @@ -369,10 +369,10 @@ public class SqlDatabase /// The result is a string that has all values separated by space character public async Task ReadDataAsync(string query) { - if (!Connection.State.HasFlag(ConnectionState.Open)) - await Connection.OpenAsync(); - var command = new SQLiteCommand(query, Connection); - var reader = await command.ExecuteReaderAsync(); + if (!_connection.State.HasFlag(ConnectionState.Open)) + await _connection.OpenAsync(); + var command = new SQLiteCommand(query, _connection); + var reader = await command.ExecuteReaderAsync(); var values = new object[reader.FieldCount]; if (reader.Read()) @@ -391,10 +391,10 @@ public class SqlDatabase /// The result is a string that has all values separated by space character public string? ReadData(string query) { - if (!Connection.State.HasFlag(ConnectionState.Open)) - Connection.Open(); - var command = new SQLiteCommand(query, Connection); - var reader = command.ExecuteReader(); + if (!_connection.State.HasFlag(ConnectionState.Open)) + _connection.Open(); + var command = new SQLiteCommand(query, _connection); + var reader = command.ExecuteReader(); var values = new object[reader.FieldCount]; if (reader.Read()) @@ -413,10 +413,10 @@ public class SqlDatabase /// The first row as separated items public async Task ReadDataArrayAsync(string query) { - if (!Connection.State.HasFlag(ConnectionState.Open)) - await Connection.OpenAsync(); - var command = new SQLiteCommand(query, Connection); - var reader = await command.ExecuteReaderAsync(); + if (!_connection.State.HasFlag(ConnectionState.Open)) + await _connection.OpenAsync(); + var command = new SQLiteCommand(query, _connection); + var reader = await command.ExecuteReaderAsync(); var values = new object[reader.FieldCount]; if (reader.Read()) @@ -436,10 +436,10 @@ public class SqlDatabase /// The first row as separated items public object[]? ReadDataArray(string query) { - if (!Connection.State.HasFlag(ConnectionState.Open)) - Connection.Open(); - var command = new SQLiteCommand(query, Connection); - var reader = command.ExecuteReader(); + if (!_connection.State.HasFlag(ConnectionState.Open)) + _connection.Open(); + var command = new SQLiteCommand(query, _connection); + var reader = command.ExecuteReader(); var values = new object[reader.FieldCount]; if (reader.Read()) @@ -459,10 +459,10 @@ public class SqlDatabase /// A list of string arrays representing the values that the query returns public async Task?> ReadAllRowsAsync(string query) { - if (!Connection.State.HasFlag(ConnectionState.Open)) - await Connection.OpenAsync(); - var command = new SQLiteCommand(query, Connection); - var reader = await command.ExecuteReaderAsync(); + if (!_connection.State.HasFlag(ConnectionState.Open)) + await _connection.OpenAsync(); + var command = new SQLiteCommand(query, _connection); + var reader = await command.ExecuteReaderAsync(); if (!reader.HasRows) return null; @@ -479,4 +479,163 @@ public class SqlDatabase return rows; } -} + + /// + /// Create a parameter for a query + /// + /// The name of the parameter + /// The value of the parameter + /// The SQLiteParameter that has the name, value and DBType set according to your inputs + private SQLiteParameter? CreateParameter(string name, object value) + { + var parameter = new SQLiteParameter(name); + parameter.Value = value; + + if (value is string) + parameter.DbType = DbType.String; + else if (value is int) + parameter.DbType = DbType.Int32; + else if (value is long) + parameter.DbType = DbType.Int64; + else if (value is float) + parameter.DbType = DbType.Single; + else if (value is double) + parameter.DbType = DbType.Double; + else if (value is bool) + parameter.DbType = DbType.Boolean; + else if (value is DateTime) + parameter.DbType = DbType.DateTime; + else if (value is byte[]) + parameter.DbType = DbType.Binary; + else if (value is Guid) + parameter.DbType = DbType.Guid; + else if (value is decimal) + parameter.DbType = DbType.Decimal; + else if (value is TimeSpan) + parameter.DbType = DbType.Time; + else if (value is DateTimeOffset) + parameter.DbType = DbType.DateTimeOffset; + else if (value is ushort) + parameter.DbType = DbType.UInt16; + else if (value is uint) + parameter.DbType = DbType.UInt32; + else if (value is ulong) + parameter.DbType = DbType.UInt64; + else if (value is sbyte) + parameter.DbType = DbType.SByte; + else if (value is short) + parameter.DbType = DbType.Int16; + else if (value is byte) + parameter.DbType = DbType.Byte; + else if (value is char) + parameter.DbType = DbType.StringFixedLength; + else if (value is char[]) + parameter.DbType = DbType.StringFixedLength; + else + return null; + + return parameter; + } + + + /// + /// Create a parameter for a query. The function automatically detects the type of the value. + /// + /// The parameter raw inputs. The Key is name and the Value is the value of the parameter + /// The SQLiteParameter that has the name, value and DBType set according to your inputs + private SQLiteParameter? CreateParameter(KeyValuePair parameterValues) => + CreateParameter(parameterValues.Key, parameterValues.Value); + + /// + /// Execute a query with parameters + /// + /// The query to execute + /// The parameters of the query + /// The number of rows that the query modified in the database + public async Task ExecuteNonQueryAsync(string query, params KeyValuePair[] parameters) + { + if (!_connection.State.HasFlag(ConnectionState.Open)) + await _connection.OpenAsync(); + + var command = new SQLiteCommand(query, _connection); + foreach (var parameter in parameters) + { + var p = CreateParameter(parameter); + if (p is not null) + command.Parameters.Add(p); + } + + return await command.ExecuteNonQueryAsync(); + } + + /// + /// Execute a query with parameters that returns a specific type of object. The function will return the first row of the result transformed into the specified type. + /// + /// The query to execute + /// The convertor function that will convert each row of the response into an object of + /// The parameters of the query + /// The return object type + /// An object of type T that represents the output of the convertor function based on the array of objects that the first row of the result has + public async Task ReadObjectOfTypeAsync (string query, Func convertor, params KeyValuePair[] parameters) + { + if (!_connection.State.HasFlag(ConnectionState.Open)) + await _connection.OpenAsync(); + + var command = new SQLiteCommand(query, _connection); + foreach (var parameter in parameters) + { + var p = CreateParameter(parameter); + if (p is not null) + command.Parameters.Add(p); + } + + var reader = await command.ExecuteReaderAsync(); + var values = new object[reader.FieldCount]; + if (reader.Read()) + { + reader.GetValues(values); + return convertor(values); + } + + return default; + } + + + /// + /// Execute a query with parameters that returns a specific type of object. The function will return a list of objects of the specified type. + /// + /// The query to execute + /// The convertor from object[] to T + /// The parameters of the query + /// The expected object type + /// A list of objects of type T that represents each line of the output of the specified query, converted to T + public async Task> ReadListOfTypeAsync(string query, Func convertor, + params KeyValuePair[] parameters) + { + if (!_connection.State.HasFlag(ConnectionState.Open)) + await _connection.OpenAsync(); + + var command = new SQLiteCommand(query, _connection); + foreach (var parameter in parameters) + { + var p = CreateParameter(parameter); + if (p is not null) + command.Parameters.Add(p); + } + + var reader = await command.ExecuteReaderAsync(); + // + if (!reader.HasRows) + return null; + + List rows = new(); + while (await reader.ReadAsync()) + { + var values = new object[reader.FieldCount]; + reader.GetValues(values); + rows.Add(convertor(values)); + } + + return rows; + } +} \ No newline at end of file diff --git a/PluginManager/Loaders/Loader.cs b/PluginManager/Loaders/Loader.cs index 803fddc..fe3a716 100644 --- a/PluginManager/Loaders/Loader.cs +++ b/PluginManager/Loaders/Loader.cs @@ -21,13 +21,13 @@ internal class Loader { internal Loader(string path, string extension) { - this.path = path; - this.extension = extension; + this.Path = path; + this.Extension = extension; } - private string path { get; } - private string extension { get; } + private string Path { get; } + private string Extension { get; } internal event FileLoadedEventHandler? FileLoaded; @@ -40,13 +40,13 @@ internal class Loader List slashCommands = new(); List commands = new(); - if (!Directory.Exists(path)) + if (!Directory.Exists(Path)) { - Directory.CreateDirectory(path); + Directory.CreateDirectory(Path); return (null, null, null); } - var files = Directory.GetFiles(path, $"*.{extension}", SearchOption.AllDirectories); + var files = Directory.GetFiles(Path, $"*.{Extension}", SearchOption.AllDirectories); foreach (var file in files) { try