From 155c9b4e7773addda58fa5125dcc2e452cb04d07 Mon Sep 17 00:00:00 2001 From: Daniel-I-Am Date: Mon, 8 Jun 2020 20:21:14 +0200 Subject: [PATCH] First version of new database system --- ChaosBot/Attribute/AssemblyController.cs | 4 +- ChaosBot/Database/Controller.cs | 333 +----------------- ChaosBot/Database/Entity/BaseEntity.cs | 30 ++ ChaosBot/Database/Entity/Points.cs | 20 +- ChaosBot/Database/Entity/Raffle.cs | 19 +- .../Entity/ServerConfigurationFlag.cs | 34 +- ChaosBot/Database/Entity/TestEntity.cs | 29 ++ ChaosBot/Database/FilterValue.cs | 16 + ChaosBot/Database/QueryBuilder.cs | 278 +++++++++++++++ ChaosBot/Database/QueryBuilderRaw.cs | 174 +++++++++ .../Repository/ConfigurationRepository.cs | 38 +- .../Database/Repository/PointsRepository.cs | 119 ++----- .../Database/Repository/RaffleRepository.cs | 128 +------ ChaosBot/Discord/Modules/PointsCommands.cs | 13 +- ChaosBot/Program.cs | 3 +- 15 files changed, 674 insertions(+), 564 deletions(-) create mode 100644 ChaosBot/Database/Entity/BaseEntity.cs create mode 100644 ChaosBot/Database/Entity/TestEntity.cs create mode 100644 ChaosBot/Database/FilterValue.cs create mode 100644 ChaosBot/Database/QueryBuilder.cs create mode 100644 ChaosBot/Database/QueryBuilderRaw.cs diff --git a/ChaosBot/Attribute/AssemblyController.cs b/ChaosBot/Attribute/AssemblyController.cs index 8d02727..2532b88 100644 --- a/ChaosBot/Attribute/AssemblyController.cs +++ b/ChaosBot/Attribute/AssemblyController.cs @@ -74,7 +74,7 @@ namespace ChaosBot.Attribute { string columnDefs = String.Join(", ", columnList.Select(c => $"{c.Item1} {c.Item2} {c.Item4}")); string query = $"CREATE TABLE {table} ({columnDefs})"; - Controller.RawQuery(query, false); + Controller.RawQuery(query, readOutput: false); } else { @@ -84,7 +84,7 @@ namespace ChaosBot.Attribute { string query = $"ALTER TABLE {table} ADD COLUMN {column.Item1} {column.Item2} {column.Item4}"; - Controller.RawQuery(query, false, true); + Controller.RawQuery(query, readOutput: false, throwError: true); } catch { diff --git a/ChaosBot/Database/Controller.cs b/ChaosBot/Database/Controller.cs index 2117ba2..b9f5d3a 100644 --- a/ChaosBot/Database/Controller.cs +++ b/ChaosBot/Database/Controller.cs @@ -26,7 +26,7 @@ namespace ChaosBot.Database /// Whether to read the output and return it as a DataFrame /// Whether to throw any exceptions or to log and shrug. /// - public static DataTable RawQuery(string query, bool readOutput = true, bool throwError = false) + public static DataTable RawQuery(string query, Dictionary parameters = null, bool readOutput = true, bool throwError = false) { DataTable dt = new DataTable(); @@ -40,11 +40,24 @@ namespace ChaosBot.Database // Start creating the command and assign the query given SqliteCommand cmd = _conn.CreateCommand(); cmd.CommandText = query; + + // Add parameters if they exist + if (parameters != null) + { + foreach (var parameter in parameters) + { + cmd.Parameters.AddWithValue(parameter.Key, parameter.Value); + } + } + + // Prepare the statement + _logger.Trace($"Database.Controller.RawQuery: Running query {cmd.CommandText}"); + cmd.Prepare(); if (readOutput) { // output needs to be read, make a reader and fill the datatable with the data - SqliteDataReader executeReader = cmd.ExecuteReader(CommandBehavior.SingleResult); + SqliteDataReader executeReader = cmd.ExecuteReader(); dt.Load(executeReader); } else @@ -71,321 +84,5 @@ namespace ChaosBot.Database return dt; } - - /// - /// Insert a list of parameters into a table in the database - /// - /// - /// string table = "SomeTable"; - /// Dictionary<string, object> parameters = new Dictionary<string, object> - /// { - /// { "someInt", 123 }, - /// { "someString", "asdf" }, - /// { "someChar", 'x' } - /// } - /// - /// Controller.InsertQuery(table, parameters); - /// - /// Table to insert into - /// List of parameters to insert to the row. Every type is a key - public static void InsertQuery(string table, Dictionary parameters) - { - try - { - using (_conn) - { - // Open the SQLite connection - _conn.Open(); - - // Start the command creation - SqliteCommand cmd = _conn.CreateCommand(); - - // Build the command bit by bit - // INSERT INTO {table} (key1, key2, key3) VALUES (@key1,@key2,@key3) - StringBuilder commandText = new StringBuilder(); - commandText.Append("INSERT INTO "); - commandText.Append(table); - commandText.Append(" ("); - - foreach (string key in parameters.Keys) - { - commandText.Append($"{key}, "); - } - - commandText.Remove(commandText.Length - 2, 2); - commandText.Append(") VALUES ("); - - foreach (string key in parameters.Keys) - { - // We use @key to indicate the parameter placeholder - commandText.Append($"@{key},"); - } - - // Remove the trailing comma - commandText.Remove(commandText.Length - 1, 1); - commandText.Append(")"); - - // Assign onto QueryCommand - cmd.CommandText = commandText.ToString(); - - // Replace all the parameters to the query - foreach (string key in parameters.Keys) - { - cmd.Parameters.AddWithValue($"@{key}", parameters.GetValueOrDefault(key)); - } - - // Prepare the parameters - cmd.Prepare(); - - // Execute, do not return anything - cmd.ExecuteNonQuery(); - - _conn.Close(); - } - } - catch (Exception ex) - { - _logger.Fatal($"Database.Controller.InsertQuery: Exception [{ex}] thrown, <[{ex.Message}]>."); - } - } - - /// - /// Update rows in the database - /// - /// - /// string table = "SomeTable"; - /// Dictionary<string, object> values = new Dictionary<string, object> - /// { - /// {"key", "newValue"} - /// }; - /// Dictionary<string, object> parameters = new Dictionary<string, object> - /// { - /// {"key", "oldValue"} - /// }; - /// Controller.UpdateQuery(table, values, parameters); - /// - /// Table to update data in - /// List of keys to set. Equivalent of SQL: SET {key} = {value} - /// List of filters to apply in the SQL WHERE condition - public static void UpdateQuery(string table, Dictionary values, Dictionary parameters) - { - try - { - using (_conn) - { - // Open SQLite connection - _conn.Open(); - - // Create the command - SqliteCommand cmd = _conn.CreateCommand(); - - // Build the commandtext - // UPDATE {table} SET {key}=@val_{key} WHERE {key}=@fil_{key} - StringBuilder commandText = new StringBuilder(); - commandText.Append("UPDATE "); - commandText.Append(table); - commandText.Append(" SET "); - - // Build the list of values - List updateList = new List(); - foreach (string key in values.Keys) - { - updateList.Add($"{key}=@val_{key} "); - cmd.Parameters.AddWithValue($@"val_{key}", values.GetValueOrDefault(key)); - } - - // Append the list of values, comma-separated - commandText.Append(string.Join(", ", updateList)); - - // Build the list of filters - List filterList = new List(); - foreach (string key in parameters.Keys) - { - filterList.Add($"{key}=@fil_{key} "); - cmd.Parameters.AddWithValue($"@fil_{key}", parameters.GetValueOrDefault(key)); - } - - if (filterList.Count > 0) - { - // Prepend with WHERE if there are parameters - commandText.Append("WHERE "); - // Append the list of filters AND separated - commandText.Append(string.Join("AND ", filterList)); - } - - // Assign the command - cmd.CommandText = commandText.ToString(); - - // Prepare the parameters - cmd.Prepare(); - - // Execute, do not return - cmd.ExecuteNonQuery(); - - // Close connection - _conn.Close(); - } - } - catch (Exception ex) - { - _logger.Fatal($"Database.Controller.UpdateQuery: Exception [{ex}] thrown, <[{ex.Message}]>."); - } - } - - public static int TransactionQuery(List cmds) - { - SqliteCommand command = _conn.CreateCommand(); - SqliteTransaction transaction; - - _conn.Open(); - - // Start a local transaction. - transaction = _conn.BeginTransaction(); - - // Must assign both transaction object and connection - // to Command object for a pending local transaction - command.Connection = _conn; - command.Transaction = transaction; - - try - { - foreach (var cmd in cmds) - { - command.CommandText = cmd.CommandText; - command.ExecuteNonQuery(); - } - - // Attempt to commit the transaction. - transaction.Commit(); - _logger.Info($"{cmds.Count} record(s) are written to database."); - } - catch (Exception ex) - { - _logger.Warn("Commit Exception Type: {0}", ex.GetType()); - _logger.Warn(" Message: {0}", ex.Message); - - // Attempt to roll back the transaction. - try - { - transaction.Rollback(); - } - catch (Exception ex2) - { - // This catch block will handle any errors that may have occurred - // on the server that would cause the rollback to fail, such as - // a closed connection. - _logger.Warn("Rollback Exception Type: {0}", ex2.GetType()); - _logger.Warn(" Message: {0}", ex2.Message); - } - - return 0; - } - - _conn.Close(); - return cmds.Count; - } - - public static DataTable SelectQuery(string table, string selectColumns = "*", Dictionary filterColumns = null, string orderByKey = null) - { - DataTable dt = new DataTable(); - - try - { - using (_conn) - { - _conn.Open(); - SqliteCommand cmd = _conn.CreateCommand(); - - string filter = null; - if (filterColumns != null) - { - List filterList = new List(); - foreach (string key in filterColumns.Keys) - { - filterList.Add($"{key} = @{key}"); - } - - filter = $"WHERE {Strings.Join(filterList.ToArray(), " AND ")}"; - - foreach (string key in filterColumns.Keys) - { - cmd.Parameters.AddWithValue($@"{key}", filterColumns.GetValueOrDefault(key)); - } - } - - string order = null; - if (orderByKey != null) - { - order = $"ORDER BY {orderByKey}"; - } - - string query = $"SELECT {selectColumns} FROM {table} {filter} {order}"; - - cmd.CommandText = query; - cmd.Prepare(); - SqliteDataReader executeReader = cmd.ExecuteReader(); - - dt.Load(executeReader); - - _conn.Close(); - } - } - catch (Exception ex) - { - _logger.Fatal($"Database.Controller.SelectQuery: Exception [{ex}] thrown, <[{ex.Message}]>."); - } - - return dt; - } - - public static void DeleteQuery(string table, Dictionary filterColumns = null, string orderByKey = null, int limit = -1, int offset = -1) - { - try - { - using (_conn) - { - _conn.Open(); - SqliteCommand cmd = _conn.CreateCommand(); - - string filter = null; - if (filterColumns != null) - { - List filterList = new List(); - foreach (string key in filterColumns.Keys) - { - filterList.Add($"{key} = @{key}"); - } - - filter = $"WHERE {Strings.Join(filterList.ToArray(), " AND ")}"; - - foreach (string key in filterColumns.Keys) - { - cmd.Parameters.AddWithValue($@"{key}", filterColumns.GetValueOrDefault(key)); - } - } - - string order = null; - if (orderByKey != null) - { - order = $"ORDER BY {orderByKey}"; - } - - string limitString = limit > 0 ? $"LIMIT {limit}" : null; - string offsetString = offset > 0 ? $"OFFSET {offset}" : null; - - string query = $"DELETE FROM {table} {filter} {order} {limitString} {offsetString}"; - - cmd.CommandText = query; - cmd.Prepare(); - cmd.ExecuteNonQuery(); - - _conn.Close(); - } - } - catch (Exception ex) - { - _logger.Fatal($"Database.Controller.DeleteQuery: Exception [{ex}] thrown, <[{ex.Message}]>."); - } - } } } \ No newline at end of file diff --git a/ChaosBot/Database/Entity/BaseEntity.cs b/ChaosBot/Database/Entity/BaseEntity.cs new file mode 100644 index 0000000..8451e7b --- /dev/null +++ b/ChaosBot/Database/Entity/BaseEntity.cs @@ -0,0 +1,30 @@ +using System; +using System.Data; +using System.Linq; +using ChaosBot.Attribute; +using ChaosBot.Database; + +namespace ChaosBot.Database.Entity +{ + public abstract class BaseEntity + { + protected BaseEntity() {} + + public abstract void SetFromRow(DataRow row); + + + protected static QueryBuilder Query() where T : BaseEntity, new() + { + var dbEntityAttribute = typeof(T).GetCustomAttributes( + typeof(DBEntity), + true + ).FirstOrDefault() as DBEntity; + if (dbEntityAttribute != null) + { + return new QueryBuilder(dbEntityAttribute.Table); + } + + throw new SystemException($"Class {typeof(T)} does not have an attribute of {typeof(DBEntity)} set"); + } + } +} \ No newline at end of file diff --git a/ChaosBot/Database/Entity/Points.cs b/ChaosBot/Database/Entity/Points.cs index a51a027..c074d88 100644 --- a/ChaosBot/Database/Entity/Points.cs +++ b/ChaosBot/Database/Entity/Points.cs @@ -1,19 +1,22 @@ +using System.Data; using ChaosBot.Attribute; namespace ChaosBot.Database.Entity { [DBEntity("PointsTable")] - public class Points + public class Points : BaseEntity { [DBPrimaryKey] [DBAutoIncrement] [DBNotNull] [DBUnique] - public int id { get; } + public int id { get; private set; } public int points { get; private set; } public string userId { get; private set; } public string guildId { get; private set; } + public Points() {} + public Points(int id, string userId, string guildId, int points) { this.id = id; @@ -28,5 +31,18 @@ namespace ChaosBot.Database.Entity this.userId = userId; this.guildId = guildId; } + + public static QueryBuilder Query() + { + return BaseEntity.Query(); + } + + public override void SetFromRow(DataRow row) + { + id = (int)row["id"]; + userId = (string)row["userId"]; + guildId = (string)row["guildId"]; + points = (int)row["points"]; + } } } \ No newline at end of file diff --git a/ChaosBot/Database/Entity/Raffle.cs b/ChaosBot/Database/Entity/Raffle.cs index d230177..4be0ade 100644 --- a/ChaosBot/Database/Entity/Raffle.cs +++ b/ChaosBot/Database/Entity/Raffle.cs @@ -1,18 +1,21 @@ +using System.Data; using ChaosBot.Attribute; namespace ChaosBot.Database.Entity { [DBEntity("RaffleTable")] - public class Raffle + public class Raffle : BaseEntity { [DBPrimaryKey] [DBAutoIncrement] [DBNotNull] [DBUnique] - public int id { get; } + public int id { get; private set; } public string userId { get; private set; } public string guildId { get; private set; } + public Raffle() {} + public Raffle(int id, string userId, string guildId) { this.id = id; @@ -25,5 +28,17 @@ namespace ChaosBot.Database.Entity this.userId = userId; this.guildId = guildId; } + + public static QueryBuilder Query() + { + return BaseEntity.Query(); + } + + public override void SetFromRow(DataRow row) + { + id = (int)row["id"]; + userId = (string)row["userid"]; + guildId = (string) row["guildid"]; + } } } \ No newline at end of file diff --git a/ChaosBot/Database/Entity/ServerConfigurationFlag.cs b/ChaosBot/Database/Entity/ServerConfigurationFlag.cs index f8d1ab7..01f79e0 100644 --- a/ChaosBot/Database/Entity/ServerConfigurationFlag.cs +++ b/ChaosBot/Database/Entity/ServerConfigurationFlag.cs @@ -1,4 +1,5 @@ using System; +using System.Data; using System.IO; using System.Text; using System.Text.Json; @@ -8,13 +9,15 @@ using ChaosBot.Attribute; namespace ChaosBot.Database.Entity { [DBEntity("ServerConfigurationFlags")] - public class ServerConfigurationFlag + public class ServerConfigurationFlag : BaseEntity { - public string key { get; } + public string key { get; private set; } - public string serializedValue { get; } + public string serializedValue { get; set; } - public long guildId { get; } + public long guildId { get; private set; } + + public ServerConfigurationFlag() {} public ServerConfigurationFlag(string key, T value, ulong guildId) { @@ -22,6 +25,29 @@ namespace ChaosBot.Database.Entity this.key = key; this.guildId = Convert.ToInt64(guildId); } + public ServerConfigurationFlag(string key, T value, long guildId) + { + this.serializedValue = Serialize(value); + this.key = key; + this.guildId = guildId; + } + public ServerConfigurationFlag(string key, long guildId) + { + this.key = key; + this.guildId = guildId; + } + + public static QueryBuilder> Query() + { + return BaseEntity.Query>(); + } + + public override void SetFromRow(DataRow row) + { + key = (string)row["key"]; + serializedValue = (string)row["serializedValue"]; + guildId = (long)row["guildId"]; + } public T GetValue() { diff --git a/ChaosBot/Database/Entity/TestEntity.cs b/ChaosBot/Database/Entity/TestEntity.cs new file mode 100644 index 0000000..195d59e --- /dev/null +++ b/ChaosBot/Database/Entity/TestEntity.cs @@ -0,0 +1,29 @@ +using System; +using System.Data; +using ChaosBot.Attribute; + +namespace ChaosBot.Database.Entity +{ + [DBEntity("TestTable")] + public class TestEntity : BaseEntity + { + public long id { get; private set; } + + public TestEntity() {} + + public TestEntity(int id) + { + this.id = id; + } + + public static QueryBuilder Query() + { + return BaseEntity.Query(); + } + + public override void SetFromRow(DataRow row) + { + id = (long)row["id"]; + } + } +} \ No newline at end of file diff --git a/ChaosBot/Database/FilterValue.cs b/ChaosBot/Database/FilterValue.cs new file mode 100644 index 0000000..23968af --- /dev/null +++ b/ChaosBot/Database/FilterValue.cs @@ -0,0 +1,16 @@ +using NLog.Filters; + +namespace ChaosBot.Database +{ + public struct FilterValue + { + public object Value; + public string Comparison; + + public FilterValue(object value, string comparison = null) + { + this.Value = value; + this.Comparison = comparison; + } + } +} \ No newline at end of file diff --git a/ChaosBot/Database/QueryBuilder.cs b/ChaosBot/Database/QueryBuilder.cs new file mode 100644 index 0000000..e970161 --- /dev/null +++ b/ChaosBot/Database/QueryBuilder.cs @@ -0,0 +1,278 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Reflection; +using System.Text; +using ChaosBot.Attribute; +using ChaosBot.Database.Entity; + +namespace ChaosBot.Database +{ + public class QueryBuilder where T : BaseEntity, new() + { + private string _table; + private bool _distinct; + private Dictionary _whereConditions; + private Dictionary _setList; + private List _orderKeys; + private List _groupByList; + private int _limit; + private int _offset; + public QueryBuilder(string table) + { + _table = table; + _distinct = false; + _whereConditions = new Dictionary(); + _setList = new Dictionary(); + _orderKeys = new List(); + _groupByList = new List(); + _limit = -1; + _offset = -1; + } + + /* + * Fetch all records from a database + */ + public List All() + { + QueryBuilderRaw query = new QueryBuilderRaw(); + DataTable dt = query + .Select() + .Append("*") + .FromTable(_table) + .Run(); + + List returnList = new List(); + foreach (DataRow row in dt.Rows) + { + T newObject = new T(); + newObject.SetFromRow(row); + returnList.Add(newObject); + } + + return returnList; + } + + /* + * Set a where condition + */ + public QueryBuilder Where(string key, object value, string comparison = null) + { + _whereConditions.Add(key, new FilterValue(value, comparison)); + return this; + } + + /* + * Set distinct + */ + public QueryBuilder Distinct() + { + _distinct = true; + return this; + } + + /* + * Set SET field + */ + public QueryBuilder Set(string key, object value) + { + _setList.Add(key, value); + return this; + } + + /* + * Set an order key + */ + public QueryBuilder OrderBy(string key) + { + _orderKeys.Add(key); + return this; + } + + /* + * Set a groupBy key + */ + public QueryBuilder GroupBy(string key) + { + _groupByList.Add(key); + return this; + } + + /* + * Limit amount of responses + */ + public QueryBuilder Limit(int amount) + { + _limit = amount; + return this; + } + + public QueryBuilder Offset(int amount) + { + _offset = amount; + return this; + } + + public QueryBuilder Limit(int limit, int offset) + { + Limit(limit); + return Offset(offset); + } + + /* + * Basic mathematical operations + */ + public int Count() + { + QueryBuilderRaw query = new QueryBuilderRaw(); + long amount = (long)query + .Select() + .Append("COUNT(*) as count") + .FromTable(_table) + .Run().Rows[0]["count"]; + return Convert.ToInt32(amount); + } + + /* + * Basic logical operations + */ + public bool Exists() + { + QueryBuilderRaw query = new QueryBuilderRaw(); + return (bool)query + .Select() + .Append("COUNT(*)>0 as present") + .FromTable(_table) + .Run().Rows[0]["present"]; + } + + public bool DoesNotExist() + { + QueryBuilderRaw query = new QueryBuilderRaw(); + return (bool)query + .Select() + .Append("COUNT(*)=0 as notPresent") + .FromTable(_table) + .Run().Rows[0]["notPresent"]; + } + + /* + * Actions + */ + public List Get() + { + List returnList = new List(); + + QueryBuilderRaw query = new QueryBuilderRaw(); + query + .Select(); + + if (_distinct) + query.Distinct(); + + query + .Append("*") + .FromTable(_table); + + if (_whereConditions.Keys.Count > 0) + query.Where(_whereConditions); + + if (_groupByList.Count > 0) + query.GroupBy(_groupByList); + + if (_orderKeys.Count > 0) + query.OrderBy(_orderKeys); + + if (_limit >= 0) + { + if (_offset >= 0) + { + query.Limit(_limit, _offset); + } + else + { + query.Limit(_limit); + } + } + + DataTable dt = query.Run(); + foreach (DataRow row in dt.Rows) + { + T newObject = new T(); + newObject.SetFromRow(row); + returnList.Add(newObject); + } + + return returnList; + } + + public T First() + { + Limit(1); + List list = Get(); + + if (list.Count > 0) + return list[0]; + return null; + } + + public void Insert(T obj) + { + Dictionary objectKeysAndValues = GetObjectKeysAndValues(obj); + QueryBuilderRaw query = new QueryBuilderRaw(); + query + .Insert() + .IntoTable(_table) + .Columns(new List(objectKeysAndValues.Keys)) + .Values(objectKeysAndValues); + + query.Run(); + } + + public void Update() + { + QueryBuilderRaw query = new QueryBuilderRaw(); + query + .Update() + .Table(_table) + .Set(_setList); + + if (_whereConditions.Keys.Count > 0) + query.Where(_whereConditions); + + query.Run(); + } + + public void Delete() + { + QueryBuilderRaw query = new QueryBuilderRaw(); + query + .Delete() + .FromTable(_table); + + if (_whereConditions.Keys.Count > 0) + query.Where(_whereConditions); + + query.Run(); + } + + /* + * Helpers + */ + private Dictionary GetObjectKeysAndValues(T obj) + { + var returnValue = new Dictionary(); + + foreach (PropertyInfo prop in typeof(T).GetProperties()) + { + string columnName = prop.Name; + object value = prop.GetValue(obj); + + if (value != null) + returnValue.Add(columnName, value); + } + + return returnValue; + } + } +} \ No newline at end of file diff --git a/ChaosBot/Database/QueryBuilderRaw.cs b/ChaosBot/Database/QueryBuilderRaw.cs new file mode 100644 index 0000000..78e915d --- /dev/null +++ b/ChaosBot/Database/QueryBuilderRaw.cs @@ -0,0 +1,174 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Text; + +namespace ChaosBot.Database +{ + public class QueryBuilderRaw + { + private StringBuilder _query; + private Dictionary _parameters; + private int keyIndex = 0; + + public QueryBuilderRaw() + { + _query = new StringBuilder(); + _parameters = new Dictionary(); + } + + public QueryBuilderRaw Append(string text) + { + _query.Append(text); + if (!text.EndsWith(" ")) + _query.Append(" "); + return this; + } + + public DataTable Run() + { + return Controller.RawQuery(_query.ToString(), _parameters); + } + + public QueryBuilderRaw Select() + { + return Append("SELECT"); + } + + public QueryBuilderRaw Insert(string alternativeAction = null) + { + Append("INSERT"); + if (alternativeAction != null) + Append(alternativeAction); + return this; + } + + public QueryBuilderRaw Update(string alternativeAction = null) + { + Append("UPDATE"); + if (alternativeAction != null) + Append(alternativeAction); + return this; + } + + public QueryBuilderRaw Delete() + { + return Append("DELETE"); + } + + public QueryBuilderRaw Distinct() + { + return Append("DISTINCT"); + } + + public QueryBuilderRaw Table(string table) + { + return Append(table); + } + + public QueryBuilderRaw FromTable(string table) + { + Append("FROM"); + return Table(table); + } + + public QueryBuilderRaw IntoTable(string table) + { + Append("INTO"); + return Table(table); + } + + public QueryBuilderRaw Columns(List columnList) + { + Append("("); + Append(String.Join(", ", columnList)); + Append(")"); + return this; + } + + public QueryBuilderRaw Values(Dictionary keyValueList) + { + Append("VALUES ("); + List parameterKeys = new List(); + foreach (KeyValuePair pair in keyValueList) + { + string parameterName = AddParameterAndReturnKey(pair.Key, pair.Value); + parameterKeys.Add(parameterName); + } + Append(string.Join(", ", parameterKeys)); + Append(")"); + return this; + } + + public QueryBuilderRaw Set(Dictionary keyValueList) + { + Append("SET"); + List parameterKeys = new List(); + foreach (KeyValuePair pair in keyValueList) + { + string parameterName = AddParameterAndReturnKey(pair.Key, pair.Value); + parameterKeys.Add($"{pair.Key} = {parameterName}"); + } + Append(string.Join(", ", parameterKeys)); + return this; + } + + public QueryBuilderRaw Where(Dictionary whereCondition) + { + Append("WHERE"); + foreach (string key in whereCondition.Keys) + { + FilterValue value = whereCondition.GetValueOrDefault(key); + string parameterName = AddParameterAndReturnKey(key, value.Value); + Append($"{key} {value.Comparison ?? "="} {parameterName}"); + } + + return this; + } + + public QueryBuilderRaw GroupBy(List groupByList) + { + Append("GROUP BY"); + return Append(string.Join(", ", groupByList)); + } + + public QueryBuilderRaw OrderBy(List orderKeys) + { + Append("ORDER BY"); + return Append(string.Join(", ", orderKeys)); + } + + public QueryBuilderRaw Limit(int limit) + { + return Append($"LIMIT {limit}"); + } + + public QueryBuilderRaw Limit(int limit, int offset) + { + return Append($"LIMIT {limit} OFFSET {offset}"); + } + + private void AddParameter(string parameterName, object value) + { + _parameters.Add(parameterName, value); + } + + private string AddParameterAndReturnKey(string key, object value) + { + string parameterKey = GetUniqueParameterName(key); + AddParameter(parameterKey, value); + return parameterKey; + } + + private string GetStaticParameterName(string key) + { + return $"@databasevalue{key}"; + } + + private string GetUniqueParameterName(string key) + { + return $"{GetStaticParameterName(key)}_{keyIndex++}"; + } + } +} \ No newline at end of file diff --git a/ChaosBot/Database/Repository/ConfigurationRepository.cs b/ChaosBot/Database/Repository/ConfigurationRepository.cs index 70b5c6b..3f0ce88 100644 --- a/ChaosBot/Database/Repository/ConfigurationRepository.cs +++ b/ChaosBot/Database/Repository/ConfigurationRepository.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Data; using ChaosBot.Database.Entity; using Microsoft.Extensions.Configuration; -using NLog; namespace ChaosBot.Database.Repository { @@ -43,13 +42,13 @@ namespace ChaosBot.Database.Repository public static string GetValueRaw(string configurationFlag, long guildId) { - Dictionary filterColumns = new Dictionary(); - filterColumns.Add("key", configurationFlag); - filterColumns.Add("guildId", guildId); - DataTable valueTable = Controller.SelectQuery(ServersTable, filterColumns: filterColumns); - if (valueTable.Rows.Count == 1) + Dictionary filterColumns = new Dictionary(); + filterColumns.Add("key", new FilterValue(configurationFlag)); + filterColumns.Add("guildId", new FilterValue(guildId)); + ServerConfigurationFlag serverConfigurationFlag = ServerConfigurationFlag.Query().Where("key", configurationFlag).Where("guildId", guildId).First(); + if (serverConfigurationFlag != null) { - return valueTable.Rows[0]["serializedValue"].ToString(); + return serverConfigurationFlag.serializedValue; } return null; @@ -81,23 +80,22 @@ namespace ChaosBot.Database.Repository { try { - Dictionary filterColumns = new Dictionary(); - filterColumns.Add("key", configurationFlag); - filterColumns.Add("guildId", guildId); - - Dictionary newValue = new Dictionary(); - newValue.Add("key", configurationFlag); - newValue.Add("guildId", guildId); - newValue.Add("serializedValue", serializedValue); - - DataTable valueTable = Controller.SelectQuery(ServersTable, "COUNT(*)", filterColumns); - if ((long) valueTable.Rows[0]["COUNT(*)"] == 1) + int matchCount = ServerConfigurationFlag.Query().Where("key", configurationFlag).Where("guildId", guildId).Count(); + if (matchCount > 0) { - Controller.UpdateQuery(ServersTable, newValue, filterColumns); + ServerConfigurationFlag.Query() + .Where("key", configurationFlag) + .Where("guildId", guildId) + .Set("key", configurationFlag) + .Set("guildId", guildId) + .Set("serializedValue", serializedValue) + .Update(); return; } - Controller.InsertQuery(ServersTable, newValue); + ServerConfigurationFlag newValue = new ServerConfigurationFlag(configurationFlag, guildId); + newValue.serializedValue = serializedValue; + ServerConfigurationFlag.Query().Insert(newValue); } catch (Exception ex) { diff --git a/ChaosBot/Database/Repository/PointsRepository.cs b/ChaosBot/Database/Repository/PointsRepository.cs index d6145bf..3d7a068 100644 --- a/ChaosBot/Database/Repository/PointsRepository.cs +++ b/ChaosBot/Database/Repository/PointsRepository.cs @@ -1,10 +1,7 @@ using System; using System.Collections.Generic; using System.Data; -using System.Drawing; -using System.Linq; using ChaosBot.Database.Entity; -using Microsoft.Data.Sqlite; namespace ChaosBot.Database.Repository { @@ -14,136 +11,66 @@ namespace ChaosBot.Database.Repository public static Points[] All() { - DataTable dataTable = Controller.SelectQuery(Table); + List pointsList = Points.Query().All(); - List pointslist = new List(); - foreach (DataRow row in dataTable.Rows) - { - int id = Convert.ToInt32((long)row["id"]); - int points = Convert.ToInt32(row["points"]); - string userId = row["userId"].ToString(); - string guildId = row["guildId"].ToString(); - - pointslist.Add(new Points(id, userId, guildId, points)); - } - - return pointslist.ToArray(); + return pointsList.ToArray(); } public static Points[] All(string guildId) { - Dictionary filterDict = new Dictionary(); - filterDict.Add("guildId", guildId); + List pointsList = Points.Query().Where("guildId", guildId).All(); - DataTable dataTable = Controller.SelectQuery(Table, filterColumns: filterDict); - - List pointslist = new List(); - foreach (DataRow row in dataTable.Rows) - { - int idFetch = Convert.ToInt32((long)row["id"]); - int pointsFetch = Convert.ToInt32(row["points"]); - string userIdFetch = row["userId"].ToString(); - string guildIdFetch = row["guildId"].ToString(); - - pointslist.Add(new Points(idFetch, userIdFetch, guildIdFetch, pointsFetch)); - } - - return pointslist.ToArray(); + return pointsList.ToArray(); } public static int Total(string userId, string guildId) { - Dictionary selectfilterDict = new Dictionary(); - selectfilterDict.Add("userId", userId); - selectfilterDict.Add("guildId", guildId); - - DataTable dt = Controller.SelectQuery(Table, "points", selectfilterDict); - - if (Convert.ToInt32(dt.Rows.Count) != 0) - return Convert.ToInt32(dt.Rows[0]["points"]); - else - { - Dictionary dict = new Dictionary(); - dict.Add("userId", userId); - dict.Add("guildId", guildId); - dict.Add("points", 0); - - Controller.InsertQuery(Table, dict); - } + Points userPoints = Points.Query().Where("userId", userId).Where("guildId", guildId).First(); + if (userPoints != null) + return userPoints.points; + + Points emptyUserPoints = new Points(userId, guildId, 0); + Points.Query().Insert(emptyUserPoints); return 0; } public static int Count(string userId, string guildId) { - Dictionary selectfilterDict = new Dictionary(); - selectfilterDict.Add("userId", userId); - selectfilterDict.Add("guildId", guildId); - - DataTable dt = Controller.SelectQuery(Table, "COUNT(*)", selectfilterDict); - - return Convert.ToInt32(dt.Rows[0]["COUNT(*)"]); + return Points.Query().Where("userId", userId).Where("guildId", guildId).Count(); } public static int Add(string userId, int points, string guildId) { - Dictionary selectfilterDict = new Dictionary(); - selectfilterDict.Add("userId", userId); - selectfilterDict.Add("guildId", guildId); - DataTable dt = Controller.SelectQuery(Table, "points", selectfilterDict); - if (Convert.ToInt32(dt.Rows.Count) != 0) + Points userPoints = Points.Query().Where("userId", userId).Where("guildId", guildId).First(); + if (userPoints != null) { - Dictionary filterDict = new Dictionary(); - filterDict.Add("userId", userId); - filterDict.Add("guildId", guildId); - - Dictionary updateDict = new Dictionary(); - updateDict.Add("points", Convert.ToInt32(dt.Rows[0]["points"]) + points); - Controller.UpdateQuery(Table, updateDict, filterDict); - return Convert.ToInt32(dt.Rows[0]["points"]) + points; + Points.Query().Where("userId", userId).Where("guildId", guildId).Set("points", userPoints.points + points).Update(); + return userPoints.points + points; } - else - { - Dictionary dict = new Dictionary(); - dict.Add("userId", userId); - dict.Add("guildId", guildId); - dict.Add("points", points); + + Points newUserPoints = new Points(userId, guildId, points); - Controller.InsertQuery(Table, dict); - return points; - } + Points.Query().Insert(newUserPoints); + return points; } public static int Remove(string userId, int points, string guildId) { - Dictionary selectfilterDict = new Dictionary(); - selectfilterDict.Add("userId", userId); - selectfilterDict.Add("guildId", guildId); - DataTable dt = Controller.SelectQuery(Table, "points", selectfilterDict); - - Dictionary filterDict = new Dictionary(); - filterDict.Add("userId", userId); - filterDict.Add("guildId", guildId); - - Dictionary updateDict = new Dictionary(); - updateDict.Add("points", Convert.ToInt32(dt.Rows[0]["points"]) - points); - Controller.UpdateQuery(Table, updateDict, filterDict); - return Convert.ToInt32(dt.Rows[0]["points"]) - points; + Points userPoints = Points.Query().Where("userId", userId).Where("guildId", guildId).First(); + Points.Query().Where("userId", userId).Where("guildId", guildId).Set("points", userPoints.points - points).Update(); + return userPoints.points - points; } public static void Delete(int userId) { - Dictionary filterDict = new Dictionary(); - filterDict.Add("userId", userId); - - Controller.DeleteQuery(Table, filterDict); + Points.Query().Where("userId", userId).Delete(); } - } } diff --git a/ChaosBot/Database/Repository/RaffleRepository.cs b/ChaosBot/Database/Repository/RaffleRepository.cs index 5cc87f1..bae7c01 100644 --- a/ChaosBot/Database/Repository/RaffleRepository.cs +++ b/ChaosBot/Database/Repository/RaffleRepository.cs @@ -17,17 +17,7 @@ namespace ChaosBot.Database.Repository /// List of raffles public static Raffle[] All() { - DataTable dataTable = Controller.SelectQuery(Table); - - List raffles = new List(); - foreach (DataRow row in dataTable.Rows) - { - int id = Convert.ToInt32((long) row["id"]); - string userId = row["userId"].ToString(); - string guildId = row["guildId"].ToString(); - - raffles.Add(new Raffle(id, userId, guildId)); - } + var raffles = Raffle.Query().All(); return raffles.ToArray(); } @@ -38,21 +28,8 @@ namespace ChaosBot.Database.Repository /// /// List of raffles public static Raffle[] All(string guildId) - { - Dictionary filterDict = new Dictionary(); - filterDict.Add("guildId", guildId); - - DataTable dataTable = Controller.SelectQuery(Table, filterColumns: filterDict); - - List raffles = new List(); - foreach (DataRow row in dataTable.Rows) - { - int idFetch = Convert.ToInt32((long) row["id"]); - string userIdFetch = row["userId"].ToString(); - string guildIdFetch = row["guildId"].ToString(); - - raffles.Add(new Raffle(idFetch, userIdFetch, guildIdFetch)); - } + { + var raffles = Raffle.Query().Where("guildId", guildId).All(); return raffles.ToArray(); } @@ -62,10 +39,8 @@ namespace ChaosBot.Database.Repository /// /// Amount of raffles public static int Count() - { - DataTable dataTable = Controller.SelectQuery(Table, "COUNT(*)"); - - return Convert.ToInt32(dataTable.Rows[0]["COUNT(*)"]); + { + return Raffle.Query().Count(); } /// @@ -74,13 +49,8 @@ namespace ChaosBot.Database.Repository /// /// Amount of raffles public static int Count(string guildId) - { - Dictionary filterDict = new Dictionary(); - filterDict.Add("guildId", guildId); - - DataTable dataTable = Controller.SelectQuery(Table, "COUNT(*)", filterDict); - - return Convert.ToInt32(dataTable.Rows[0]["COUNT(*)"]); + { + return Raffle.Query().Where("guildId", guildId).Count(); } /// @@ -90,14 +60,8 @@ namespace ChaosBot.Database.Repository /// /// Amount of raffles public static int Count(string userId, string guildId) - { - Dictionary filterDict = new Dictionary(); - filterDict.Add("userId", userId); - filterDict.Add("guildId", guildId); - - DataTable dataTable = Controller.SelectQuery(Table, "COUNT(*)", filterDict); - - return Convert.ToInt32(dataTable.Rows[0]["COUNT(*)"]); + { + return Raffle.Query().Where("userId", userId).Where("guildId", guildId).Count(); } /// @@ -106,22 +70,9 @@ namespace ChaosBot.Database.Repository /// /// List of raffles public static Raffle[] SelectUser(string userId) - { - Dictionary filterDict = new Dictionary(); - filterDict.Add("userId", userId); + { + List raffles = Raffle.Query().Where("userId", userId).Get(); - DataTable dataTable = Controller.SelectQuery(Table, filterColumns: filterDict); - - List raffles = new List(); - foreach (DataRow row in dataTable.Rows) - { - int id = Convert.ToInt32((long) row["id"]); - string userIdFetch = row["userId"].ToString(); - string guildIdFetch = row["guildId"].ToString(); - - raffles.Add(new Raffle(id, userIdFetch, guildIdFetch)); - } - return raffles.ToArray(); } @@ -132,22 +83,8 @@ namespace ChaosBot.Database.Repository /// /// List of raffles public static Raffle[] SelectUser(string userId, string guildId) - { - Dictionary filterDict = new Dictionary(); - filterDict.Add("userId", userId); - filterDict.Add("guildId", guildId); - - DataTable dataTable = Controller.SelectQuery(Table, filterColumns: filterDict); - - List raffles = new List(); - foreach (DataRow row in dataTable.Rows) - { - int id = Convert.ToInt32((long) row["id"]); - string userIdFetch = row["userId"].ToString(); - string guildIdFetch = row["guildId"].ToString(); - - raffles.Add(new Raffle(id, userIdFetch, guildIdFetch)); - } + { + List raffles = Raffle.Query().Where("userId", userId).Where("guildId", guildId).Get(); return raffles.ToArray(); } @@ -158,12 +95,7 @@ namespace ChaosBot.Database.Repository /// public static void Insert(Raffle raffle) { - Dictionary dict = new Dictionary(); - - dict.Add("userId", raffle.userId); - dict.Add("guildId", raffle.guildId); - - Controller.InsertQuery(Table, dict); + Raffle.Query().Insert(raffle); } /// @@ -174,12 +106,7 @@ namespace ChaosBot.Database.Repository { foreach (var raf in raffles) { - Dictionary dict = new Dictionary(); - - dict.Add("userId", raf.userId); - dict.Add("guildId", raf.guildId); - - Controller.InsertQuery(Table, dict); + Raffle.Query().Insert(raf); } } @@ -190,17 +117,7 @@ namespace ChaosBot.Database.Repository /// Random raffle public static Raffle PickRandom(string guildId) { - Dictionary filterDict = new Dictionary(); - filterDict.Add("guildId", guildId); - - DataTable dataTable = Controller.SelectQuery(Table, "*", filterDict, "RANDOM()"); - - if (dataTable.Rows.Count == 0) return null; - DataRow row = dataTable.Rows[0]; - int idFetch = Convert.ToInt32((long)row["id"]); - string userIdFetch = row["userId"].ToString(); - string guildIdFetch = row["guildId"].ToString(); - return new Raffle(idFetch, userIdFetch, guildIdFetch); + return Raffle.Query().Where("guildId", guildId).OrderBy("RANDOM()").First(); } /// @@ -209,11 +126,7 @@ namespace ChaosBot.Database.Repository /// public static void ClearRaffle(string guildId) { - Dictionary filterDict = new Dictionary(); - - filterDict.Add("guildId", guildId); - - Controller.DeleteQuery(Table, filterDict); + Raffle.Query().Where("guildId", guildId).Delete(); } /// @@ -221,11 +134,8 @@ namespace ChaosBot.Database.Repository /// /// public static void Delete(int id) - { - Dictionary filterDict = new Dictionary(); - filterDict.Add("id", id); - - Controller.DeleteQuery(Table, filterDict); + { + Raffle.Query().Where("id", id).Delete(); } } } \ No newline at end of file diff --git a/ChaosBot/Discord/Modules/PointsCommands.cs b/ChaosBot/Discord/Modules/PointsCommands.cs index 6c5beb7..b67c733 100644 --- a/ChaosBot/Discord/Modules/PointsCommands.cs +++ b/ChaosBot/Discord/Modules/PointsCommands.cs @@ -97,20 +97,15 @@ namespace ChaosBot.Discord.Modules public async Task DeletePoints(string userMention) { ulong userId = Convert.ToUInt64(userMention.Substring(3, userMention.Length - 4)); - Dictionary filterColumns = new Dictionary - { - { "userId", userId }, - { "guildId", Context.Guild.Id } - }; int matches = PointsRepository.Count(userId.ToString(), Context.Guild.Id.ToString()); if (matches > 0) { - Controller.DeleteQuery("PointsTable", filterColumns); + Points.Query().Where("userId", userId).Where("guildId", Context.Guild.Id).Delete(); - string message = $"{Context.User.Mention} has removed <@{userId}> from the database."; - await ReplyAsync(message, false); - _logger.Info($"PointsCommands.DeletePoints: {message}"); + string message = $"{Context.User.Mention} has removed <@{userId}> from the database."; + await ReplyAsync(message, false); + _logger.Info($"PointsCommands.DeletePoints: {message}"); } else { diff --git a/ChaosBot/Program.cs b/ChaosBot/Program.cs index 3ec4c58..9338aec 100644 --- a/ChaosBot/Program.cs +++ b/ChaosBot/Program.cs @@ -36,7 +36,6 @@ namespace ChaosBot */ _logger = Logging.GenLog(); - /* * Attempt to load our custom assemblies */ @@ -46,7 +45,7 @@ namespace ChaosBot * Initialize the Discord Client and Login */ _logger.Info($"Starting Up {ConfigurationRepository.GetValue("Bot:Name")} v{ConfigurationRepository.GetValue("Bot:Version")}"); - + var discordBot = DiscordConnect.StartUp(); WebServer.WebServer.Start(new string[]{}); await discordBot;