using eu.railduction.netcore.dll.Database_Attribute_System.Attributes; using System; using System.Collections.Generic; using System.Reflection; using System.Text; namespace eu.railduction.netcore.dll.Database_Attribute_System { public class ClassAction { private static Dictionary initiatedClassTypes = new Dictionary() { }; /// /// Initiates the attribute-system and preloads all necessary information /// INFO: Will initiate necessary foreignObjects recursively! /// If an class is already initiated, it will be ignored! /// /// The classType to preload /// DbObject-attribute corresponding to the class public static DbObject Init(Type classType) { DbObject cachedDbObject; initiatedClassTypes.TryGetValue(classType, out cachedDbObject); if (cachedDbObject == null) { // Check if given class is marked as dbObject if (!(classType.GetCustomAttribute(typeof(DbObject), true) is DbObject dbObject)) throw new InvalidOperationException($"Cannot init '{classType.Name}'. Missing Attribute 'DbObject'"); dbObject.Init(classType); // Init dbObject initiatedClassTypes.Add(classType, dbObject); // Set it to the list cachedDbObject = dbObject; } return cachedDbObject; } /// /// Fills an given dbObject with given data /// Data-attribute-names and class-fieldNames have to match! (non case-sensitive) /// /// /// Given object (marked with Db-attributes) /// The data public static void FillObject(T classObject, Dictionary data) { Type classType = classObject.GetType(); // Read dbObject-attribute DbObject dbObject = ClassAction.Init(classType); // Iterate through data foreach (KeyValuePair data_keySet in data) { // Interate through class-fields foreach (BaseAttribute baseAttribute in dbObject.baseAttributes) { // Remove any leading dots and table-specifiers string dbAttName = data_keySet.Key; if (dbAttName.Contains(".")) { string[] dbAttNameSplit = dbAttName.Split('.'); // Split at the '.' dbAttName = dbAttNameSplit[dbAttNameSplit.Length - 1]; // Copy the ending text } // If its a match, set the value if (baseAttribute._attributeName.ToLower() == data_keySet.Key.ToLower()) { object value = data_keySet.Value; //if (baseAttribute.parentField.FieldType == typeof(Guid)) value = new Guid((string)value); // If its a guid, i need to convert baseAttribute.parentField.SetValue(classObject, value); break; } } } } /// /// Gets an dbObject by primaryKey/s /// /// /// Given object (marked with Db-attributes) /// Function to handle query-calls - Has to return Dictionary[attributeName, attributeValue] public static T GetByPrimaryKey(Type classType, object primaryKeyValue, Func>> queryExecutor) where T : new() { Dictionary primaryKeyData = new Dictionary() { }; primaryKeyData.Add(null, primaryKeyValue); return GetByPrimaryKey(classType, primaryKeyData, queryExecutor); } public static T GetByPrimaryKey(Type classType, string primaryKeyName, object primaryKeyValue, Func>> queryExecutor) where T : new() { Dictionary primaryKeyData = new Dictionary() { }; primaryKeyData.Add(primaryKeyName, primaryKeyValue); return GetByPrimaryKey(classType, primaryKeyData, queryExecutor); } public static T GetByPrimaryKey(Type classType, Dictionary primaryKeyData, Func>> queryExecutor) where T: new() { // Create new empty object T obj = new T(); // Read dbObject-attribute DbObject dbObject = ClassAction.Init(classType); // iterate thru them to check and fill object foreach (DbPrimaryKey primaryKeyAtt in dbObject.primaryKeyAttributes) { bool dataMatchFound = false; // Now search the corresponding primaryKeyData foreach (KeyValuePair primaryKey in primaryKeyData) { // primaryKey matches if(primaryKeyAtt._attributeName.ToLower() == primaryKey.Key.ToLower()) { // Set data primaryKeyAtt.parentField.SetValue(obj, primaryKey.Value); dataMatchFound = true; break; } } // If no data was found matching this field if (!dataMatchFound) throw new InvalidOperationException($"Cannot create object with primaryKeyData. No data assigned to field '{primaryKeyAtt.parentField.Name}'"); } ResolveByPrimaryKey(obj, queryExecutor); return obj; } // ---- /// /// Gets all dbObjects of class/table /// /// /// Given object (marked with Db-attributes) /// Function to handle query-calls - Has to return Dictionary[attributeName, attributeValue] public static List GetList(Type classType, Func>> queryExecutor) where T : new() { // Read dbObject - attribute DbObject dbObject = ClassAction.Init(classType); string query = QueryBuilder.SelectByAttribute(dbObject._tableName); // Generate query List> dataSet = queryExecutor(query); // Execute List objs = new List() { }; foreach (Dictionary data in dataSet) { T obj = new T(); // New object FillObject(obj, data); // Fill it objs.Add(obj); // Add to list } return objs; // Return list } /// /// Gets an dbObject by custom where-clause /// /// /// Given object (marked with Db-attributes) /// Custom where-clause params attached to query (SELECT * FROM tableName WHERE whereClause) /// Function to handle query-calls - Has to return Dictionary[attributeName, attributeValue] public static List GetListWithWhere(Type classType, Func>> queryExecutor, params object[] whereClause) where T : new() { // Read dbObject - attribute DbObject dbObject = ClassAction.Init(classType); string query = QueryBuilder.SelectWithWhere(dbObject._tableName, whereClause); // Generate query List> dataSet = queryExecutor(query); // Execute List objs = new List() { }; foreach (Dictionary data in dataSet) { T obj = new T(); // New object FillObject(obj, data); // Fill it objs.Add(obj); // Add to list } return objs; // Return list } /// /// Gets an dbObject by full query /// /// /// Given object (marked with Db-attributes) /// Custom sql-query /// Function to handle query-calls - Has to return Dictionary[attributeName, attributeValue] public static List GetListWithQuery(Type classType, Func>> queryExecutor, params object[] customQuery) where T : new() { // Read dbObject - attribute DbObject dbObject = ClassAction.Init(classType); string query = QueryBuilder.BuildQuery(customQuery); List> dataSet = queryExecutor(query); // Execute List objs = new List() { }; foreach (Dictionary data in dataSet) { T obj = new T(); // New object FillObject(obj, data); // Fill it objs.Add(obj); // Add to list } return objs; // Return list } /// /// Gets a list of dbObjects by attribute/s /// /// /// Type of class /// class-fields for select /// Function to handle query-calls - Has to return Dictionary[attributeName, attributeValue] /// List of dbObjects public static List GetListByAttribute(Type classType, Dictionary fields, Func>> queryExecutor) where T : new() { // Read dbObject-attribute DbObject dbObject = ClassAction.Init(classType); Function.ConvertAttributeToDbAttributes(classType, fields); string query = QueryBuilder.SelectByAttribute(dbObject._tableName, fields); // Generate query List> dataSet = queryExecutor(query); // Execute List objs = new List() { }; foreach(Dictionary data in dataSet) { T obj = new T(); // New object FillObject(obj, data); // Fill it objs.Add(obj); // Add to list } return objs; // Return list } // ----- /// /// Resolves dbObject by primaryKey/s /// Object needs to have primaryKey/s set! /// /// /// Given object (marked with Db-attributes) /// Function to handle query-calls - Has to return Dictionary[attributeName, attributeValue] public static void ResolveByPrimaryKey(T classObject, Func>> queryExecutor) { string query = QueryBuilder.SelectByPrimaryKey(classObject); // Generate query List> dataSet = queryExecutor(query); // Execute if (dataSet.Count == 0) throw new InvalidOperationException($"Cannot fetch '{typeof(T).Name}' by primary key/s. No results!"); FillObject(classObject, dataSet[0]); // Fill the object } /// /// Resolves all foreignKeys with the database /// Only works if the foreignKey is single (not assembled)! /// /// /// Given object (marked with Db-attributes) /// Function to handle query-calls - Has to return Dictionary[attributeName, attributeValue] /// Determents how deep resolving will be executed public static void ResolveForeignKeys(T classObject, Func>> queryExecutor, int max_depth = 1) where T: new() { Type classType = classObject.GetType(); // Read dbObject-attribute DbObject dbObject = ClassAction.Init(classType); foreach (DbForeignObject foreignObjectAtt in dbObject.foreignObjectAttributes) { object foreignObject_value = foreignObjectAtt.parentField.GetValue(classObject); // When its empty, get it if(foreignObject_value == null) { foreignObject_value = GetByPrimaryKey(classType, foreignObjectAtt.foreignKeyAttribute.parentField.GetValue(classObject), queryExecutor); } // Recursive resolving if (max_depth - 1 > 0) { ResolveForeignKeys(foreignObject_value, queryExecutor, max_depth - 1); } } } } }