diff --git a/ChaosBot/Attribute/AssemblyController.cs b/ChaosBot/Attribute/AssemblyController.cs new file mode 100644 index 0000000..b7fb2de --- /dev/null +++ b/ChaosBot/Attribute/AssemblyController.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using ChaosBot.Database; +using Microsoft.Data.Sqlite; +using NLog; + +namespace ChaosBot.Attribute +{ + public static class AssemblyController + { + private static Logger _logger = Program._logger; + public static void Register() + { + Assembly dbEntityAssembly = Assembly.GetAssembly(typeof(DBEntity)); + if (dbEntityAssembly != null) + { + foreach (Type type in dbEntityAssembly.GetTypes()) + { + if (type.GetCustomAttributes(typeof(DBEntity), true).Length > 0) + { + // type == all classes that have [DBEntity(...)] + + // Get a reference to the attribute + DBEntity dbEntity = type.GetCustomAttributes(typeof(DBEntity)).Cast().First(); + + string table = dbEntity.Table; + // TODO: Generate this in one go instead of many separate commands + Controller.RawQuery($"CREATE TABLE IF NOT EXISTS {table} (id INTEGER NOT NULL CONSTRAINT {table}_pk PRIMARY KEY AUTOINCREMENT)"); + + foreach (MethodInfo field in type.GetMethods()) + { + if (field.Name.StartsWith("get_")) + { + try + { + // TODO: Use an attribute to detect primary autoincrement key + if (field.Name == "id") + { + Controller.RawQuery($"CREATE UNIQUE INDEX {table}_id_uindex ON {table} (id)", false, true); + continue; + } + + string fieldname = field.Name.Substring(4); + string fieldType = DBEntity.DataTypes.GetValueOrDefault(field.ReturnType).ToString(); + string fieldModifiers = null; + + string query = $"ALTER TABLE {table} ADD COLUMN {fieldname} {fieldType} {fieldModifiers}"; + Controller.RawQuery(query, false, true); + } + catch (Exception ex) + { + if (ex is SqliteException sqliteException) + { + if (!sqliteException.Message.Contains("duplicate column name")) + { + _logger.Fatal($"AssemblyController.Register: Exception [{ex}] thrown, <[{ex.Message}]>."); + } + } + else + { + _logger.Error($"AssemblyController.Register: Exception [{ex}] thrown, <[{ex.Message}]>."); + } + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/ChaosBot/Attribute/DBEntity.cs b/ChaosBot/Attribute/DBEntity.cs new file mode 100644 index 0000000..4cf1804 --- /dev/null +++ b/ChaosBot/Attribute/DBEntity.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using Microsoft.Data.Sqlite; + +namespace ChaosBot.Attribute +{ + [AttributeUsage(AttributeTargets.Class)] + public class DBEntity : System.Attribute + { + public readonly string Table; + + public static readonly Dictionary DataTypes = new Dictionary + { + {typeof(bool), SqliteType.Integer}, + {typeof(byte), SqliteType.Integer}, + {typeof(byte[]), SqliteType.Blob}, + {typeof(char), SqliteType.Text}, + {typeof(DateTime), SqliteType.Text}, + {typeof(DateTimeOffset), SqliteType.Text}, + {typeof(Decimal), SqliteType.Text}, + {typeof(Double), SqliteType.Real}, + {typeof(Guid), SqliteType.Text}, + {typeof(Int16), SqliteType.Integer}, + {typeof(Int32), SqliteType.Integer}, + {typeof(Int64), SqliteType.Integer}, + {typeof(SByte), SqliteType.Integer}, + {typeof(Single), SqliteType.Real}, + {typeof(String), SqliteType.Text}, + {typeof(TimeSpan), SqliteType.Text}, + {typeof(UInt16), SqliteType.Integer}, + {typeof(UInt32), SqliteType.Integer}, + {typeof(UInt64), SqliteType.Integer} + }; + + public DBEntity(string table) + { + this.Table = table; + } + } +} \ No newline at end of file diff --git a/ChaosBot/Database/Controller.cs b/ChaosBot/Database/Controller.cs index f7599fc..103672a 100644 --- a/ChaosBot/Database/Controller.cs +++ b/ChaosBot/Database/Controller.cs @@ -15,7 +15,7 @@ namespace ChaosBot.Database static SqliteConnection _conn = new SqliteConnection($"Data Source={System.IO.Directory.GetCurrentDirectory()}/{Program.Cfg.GetValue("Bot:Database")}"); private static Logger _logger = Program._logger; - public static DataTable RawQuery(string query) + public static DataTable RawQuery(string query, bool readOutput = true, bool throwError = false) { DataTable dt = new DataTable(); @@ -27,15 +27,27 @@ namespace ChaosBot.Database SqliteCommand cmd = _conn.CreateCommand(); cmd.CommandText = query; - SqliteDataReader executeReader = cmd.ExecuteReader(CommandBehavior.SingleResult); - - dt.Load(executeReader); - + + if (readOutput) + { + SqliteDataReader executeReader = cmd.ExecuteReader(CommandBehavior.SingleResult); + dt.Load(executeReader); + } + else + { + cmd.ExecuteNonQuery(); + } + _conn.Close(); } } catch (Exception ex) { + if (throwError) + { + throw; + } + _logger.Fatal($"Controllers.DBWork.RawQuery: Exception [{ex}] thrown, <[{ex.Message}]>."); } diff --git a/ChaosBot/Database/Entity/Raffle.cs b/ChaosBot/Database/Entity/Raffle.cs index 9abba6c..de8f954 100644 --- a/ChaosBot/Database/Entity/Raffle.cs +++ b/ChaosBot/Database/Entity/Raffle.cs @@ -1,5 +1,8 @@ +using ChaosBot.Attribute; + namespace ChaosBot.Database.Entity { + [DBEntity("RaffleTable")] public class Raffle { public int id { get; } diff --git a/ChaosBot/Program.cs b/ChaosBot/Program.cs index ed60476..e882adb 100644 --- a/ChaosBot/Program.cs +++ b/ChaosBot/Program.cs @@ -1,7 +1,9 @@ using NLog; using System; +using System.Reflection; using ChaosBot.Discord; using System.Threading.Tasks; +using ChaosBot.Attribute; using Microsoft.Extensions.Configuration; @@ -34,6 +36,12 @@ namespace ChaosBot * Initialize the _logger for logging purposes */ _logger = Logging.GenLog(); + + + /* + * Attempt to load our custom assemblies + */ + AssemblyController.Register(); /* * Initialize the Discord Client and Login