From 111b3bf7cedebf282916f52ab47220a1af86cd5b Mon Sep 17 00:00:00 2001 From: Railz Date: Tue, 9 Jul 2019 14:01:06 +0200 Subject: [PATCH] Added DbReverseForeignObject Removed method SqlSerialise(DateTime ...) --- .../Attributes/DbObject.cs | 6 ++ .../Attributes/DbReverseForeignObject.cs | 60 +++++++++++++++++++ Database-Attribute_System/ClassAction.cs | 60 ++++++++++++++++++- .../internal/Function.cs | 17 ++---- 4 files changed, 129 insertions(+), 14 deletions(-) create mode 100644 Database-Attribute_System/Attributes/DbReverseForeignObject.cs diff --git a/Database-Attribute_System/Attributes/DbObject.cs b/Database-Attribute_System/Attributes/DbObject.cs index 5cbb993..9adfb74 100644 --- a/Database-Attribute_System/Attributes/DbObject.cs +++ b/Database-Attribute_System/Attributes/DbObject.cs @@ -20,6 +20,7 @@ namespace eu.railduction.netcore.dll.Database_Attribute_System.Attributes public List attributeAttributes = new List() { }; public List foreignKeyAttributes = new List() { }; public List foreignObjectAttributes = new List() { }; + public List reverseForeignObjectAttributes = new List() { }; /// /// Marks variable as database-table @@ -68,6 +69,11 @@ namespace eu.railduction.netcore.dll.Database_Attribute_System.Attributes this.foreignObjectAttributes.Add(fobj); } + else if (fi.GetCustomAttribute(typeof(DbReverseForeignObject), true) is DbReverseForeignObject rfobj) // ReverseForeignObjects + { + rfobj.Init(fi, this); + this.reverseForeignObjectAttributes.Add(rfobj); + } } catch(InvalidOperationException ex) { diff --git a/Database-Attribute_System/Attributes/DbReverseForeignObject.cs b/Database-Attribute_System/Attributes/DbReverseForeignObject.cs new file mode 100644 index 0000000..d9760fa --- /dev/null +++ b/Database-Attribute_System/Attributes/DbReverseForeignObject.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; + +namespace eu.railduction.netcore.dll.Database_Attribute_System.Attributes +{ + [AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)] + public class DbReverseForeignObject : Attribute + { + public Type foreignObjectType; + + public string _foreignKeyName; + public DbForeignKey foreignKeyAttribute; + + public FieldInfo parentField; + public DbObject classAttribute; + + /// + /// Marks variable as reverse-foreign-object of an dbObject + /// + /// Fieldname of primaryKey associated with the reverseForeignObject (null if same as primary key) [Only works with 1 primaryKey] + public DbReverseForeignObject(string foreignKeyName = null) + { + this._foreignKeyName = foreignKeyName; + } + + public void Init(FieldInfo fi, DbObject classAttribute) + { + this.parentField = fi; + this.classAttribute = classAttribute; + this.foreignObjectType = fi.FieldType; + + // Init foreign-object class + DbObject foreignClassAttribute = ClassAction.Init(this.foreignObjectType); + + if (classAttribute.primaryKeyAttributes.Count < 1) throw new InvalidOperationException($"'{classAttribute.parentClassType.Name}' does not have a primaryKey."); + if (classAttribute.primaryKeyAttributes.Count > 1) throw new InvalidOperationException($"ReverseForeignObject does not support multiple primaryKeys."); + // Get primaryKey name if none is set + if (_foreignKeyName == null) _foreignKeyName = classAttribute.primaryKeyAttributes[0]._attributeName; + + // Check if my primary-key is set in the foreign-class as foreignKey + DbPrimaryKey primaryKey = classAttribute.primaryKeyAttributes[0]; + foreach (DbForeignKey foreignKey in foreignClassAttribute.foreignKeyAttributes) + { + if (primaryKey._attributeName.ToLower() == foreignKey._attributeName.ToLower()) // Name matches + if (primaryKey.parentField.GetType() == foreignKey.parentField.GetType()) // Type matches + { + foreignKeyAttribute = foreignKey; + } + else + // Same name, but wrong type + throw new InvalidOperationException($"ForeignObject='{foreignClassAttribute.parentClassType.Name}' has invalid type foreignKey='{foreignKey.parentField.Name}' for object='{classAttribute.parentClassType.Name}' with primaryKey='{primaryKey.parentField.Name}'."); + } + // No match + if (foreignKeyAttribute == null) throw new InvalidOperationException($"ForeignObject='{foreignClassAttribute.parentClassType.Name}' is missing foreignKey for object='{classAttribute.parentClassType.Name}' with primaryKey='{primaryKey.parentField.Name}'."); + } + } +} diff --git a/Database-Attribute_System/ClassAction.cs b/Database-Attribute_System/ClassAction.cs index 27750b3..d55bfd6 100644 --- a/Database-Attribute_System/ClassAction.cs +++ b/Database-Attribute_System/ClassAction.cs @@ -1,5 +1,6 @@ using eu.railduction.netcore.dll.Database_Attribute_System.Attributes; using System; +using System.Collections; using System.Collections.Generic; using System.Reflection; using System.Text; @@ -126,7 +127,7 @@ namespace eu.railduction.netcore.dll.Database_Attribute_System } // 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}'"); + if (!dataMatchFound) throw new InvalidOperationException($"PrimaryKey='{primaryKeyAtt.parentField.Name}' is missing."); } ResolveByPrimaryKey(obj, queryExecutor); @@ -276,22 +277,75 @@ namespace eu.railduction.netcore.dll.Database_Attribute_System // Read dbObject-attribute DbObject dbObject = ClassAction.Init(classType); + // Resolve foreignObjects + foreach (DbForeignObject foreignObjectAtt in dbObject.foreignObjectAttributes) { object foreignObject_value = foreignObjectAtt.parentField.GetValue(classObject); - // When its empty, get it + // When its empty, get it & set it if(foreignObject_value == null) { foreignObject_value = GetByPrimaryKey(classType, foreignObjectAtt.foreignKeyAttribute.parentField.GetValue(classObject), queryExecutor); + foreignObjectAtt.parentField.SetValue(classObject, foreignObject_value); } // Recursive resolving - if (max_depth - 1 > 0) + if (max_depth > 1) { + // Go recursively into the next class ResolveForeignKeys(foreignObject_value, queryExecutor, max_depth - 1); } } + + // Resolve reverseForeignObjects + foreach (DbReverseForeignObject reverseForeignObjectAtt in dbObject.reverseForeignObjectAttributes) + { + object reverseForeignObject_value = reverseForeignObjectAtt.parentField.GetValue(classObject); + + // When its empty, get it & set it + if (reverseForeignObject_value == null) + { + // Generate & set attribute-set + Dictionary attributes = new Dictionary(); + attributes.Add(reverseForeignObjectAtt._foreignKeyName, dbObject.primaryKeyAttributes[0].parentField.GetValue(classObject)); + + List values = GetListByAttribute(reverseForeignObjectAtt.foreignKeyAttribute.classAttribute.parentClassType, attributes, queryExecutor); + + if(values.Count == 0) throw new InvalidOperationException($"'{reverseForeignObjectAtt.parentField.Name}' could not been resolved. ReverseForeignObject returned '{values.Count}' values."); + + // Check for type to determen 1:1 or 1:m + Type reverseForeignObject_type = reverseForeignObjectAtt.parentField.GetType(); + if (reverseForeignObject_type is IList && reverseForeignObject_type.IsGenericType) // List, so 1:m + { + reverseForeignObject_value = values; + } + else // Not list, so 1:1 + { + if (values.Count > 1) throw new InvalidOperationException($"'{reverseForeignObjectAtt.parentField.Name}' could not been resolved as ReverseForeignObject returned '{values.Count}' values. (Is it 1:1 instead of 1:m?)"); + reverseForeignObject_value = values[0]; + } + reverseForeignObjectAtt.parentField.SetValue(classObject, reverseForeignObject_value); + } + + // Recursive resolving + if (max_depth > 1) + { + if (reverseForeignObject_value is IList) // 1:m + { + // If we have a list of objects, we need to recursively go into each one + foreach(object value in (IList)reverseForeignObject_value) + { + ResolveForeignKeys(value, queryExecutor, max_depth - 1); + } + } + else // 1:1 + { + // Go recursively into the next class + ResolveForeignKeys(reverseForeignObject_value, queryExecutor, max_depth - 1); + } + } + } } } } diff --git a/Database-Attribute_System/internal/Function.cs b/Database-Attribute_System/internal/Function.cs index b85fd87..f6baead 100644 --- a/Database-Attribute_System/internal/Function.cs +++ b/Database-Attribute_System/internal/Function.cs @@ -14,11 +14,6 @@ namespace eu.railduction.netcore.dll.Database_Attribute_System return str_cpy; } - public static string SqlSerialise(DateTime dt, string format = "yyyy-MM-dd HH:mm:ss") - { - return dt.ToString(format); - } - // Recursive object[] copying internal static void RecursiveParameterCopying(ref List paramz, object[] objects) { @@ -124,24 +119,24 @@ namespace eu.railduction.netcore.dll.Database_Attribute_System public static string SqlSerialise(object obj) { - if (obj == null) // Handle null + if (obj == null || obj is DBNull) // Handle null { return "null"; } - else if (obj.GetType() == typeof(string)) // Handle strings + else if (obj is string) // Handle strings { return "'" + SqlEscape((string)obj) + "'"; // wrap in sql-brackets and escape sql, if any } - else if (obj.GetType() == typeof(byte) || obj.GetType() == typeof(int) || obj.GetType() == typeof(float) || obj.GetType() == typeof(double)) // Handle int, float & double + else if (obj is byte || obj is int || obj is float || obj is double) // Handle int, float & double { return obj.ToString().Replace(",", "."); // just format to string and form comma to sql-comma } - else if (obj.GetType() == typeof(DateTime)) // Handle DateTime + else if (obj is DateTime) // Handle DateTime { DateTime dateTime = (DateTime)obj; - return "'" + SqlSerialise(dateTime) + "'"; // wrap in sql-brackets and convert to sql-datetime + return "'" + dateTime.ToString("yyyy-MM-dd HH:mm:ss") + "'"; // wrap in sql-brackets and convert to sql-datetime } - else if (obj.GetType() == typeof(Guid)) // Handle Guid + else if (obj is Guid) // Handle Guid { string guid = ((Guid)obj).ToString(); // Get Guid as string return "'" + guid + "'"; // wrap in sql-brackets