DatabaseSchema.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. using System;
  2. using System.Collections.Concurrent;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Reflection;
  6. namespace InABox.Core
  7. {
  8. public static class DatabaseSchema
  9. {
  10. // {className: {propertyName: property}}
  11. private static Dictionary<string, Dictionary<string, IProperty>> _Properties { get; }
  12. = new Dictionary<string, Dictionary<string, IProperty>>();
  13. /*private static BaseEditor GetPropertyEditor(Type type, PropertyInfo property, string propertyName)
  14. {
  15. BaseEditor editor = new NullEditor();
  16. var parts = propertyName.Split('.');
  17. var caption = "";
  18. var page = "";
  19. int? sequence = null;
  20. for (var i = 0; i < parts.Length; i++)
  21. {
  22. var column = string.Join(".", parts.Take(i + 1));
  23. var prop = CoreUtils.GetProperty(type, column);
  24. if (column.Equals(propertyName))
  25. {
  26. editor = property.GetEditor() ?? new NullEditor();
  27. }
  28. else
  29. {
  30. var pedit = prop.GetEditor();
  31. if (pedit is NullEditor) return pedit;
  32. }
  33. editor = editor == null ? new NullEditor() : (editor.Clone() as BaseEditor)!;
  34. var capattr = prop.GetCustomAttribute<Caption>();
  35. var subcap = capattr != null ? capattr.Text : parts[i];
  36. var path = capattr == null || capattr.IncludePath;
  37. if (!string.IsNullOrWhiteSpace(subcap))
  38. caption = string.IsNullOrWhiteSpace(caption) || path == false ? subcap : string.Format("{0} {1}", caption, subcap);
  39. if (string.IsNullOrWhiteSpace(page))
  40. {
  41. var pageattr = prop.GetCustomAttribute<EditorSequence>();
  42. if (pageattr != null)
  43. {
  44. page = pageattr.Page;
  45. sequence = pageattr.Sequence;
  46. }
  47. }
  48. }
  49. editor.Caption = caption;
  50. editor.Page = page;
  51. editor.EditorSequence = sequence ?? 999;
  52. return editor;
  53. }*/
  54. private static void RegisterProperties(Type master, Type type, string prefix, StandardProperty? parent)
  55. {
  56. try
  57. {
  58. var classname = master.EntityName();
  59. var properties = CoreUtils.PropertyList(
  60. type,
  61. x => !x.PropertyType.IsInterface &&
  62. x.GetGetMethod()?.IsPublic == true &&
  63. (x.DeclaringType.IsSubclassOf(typeof(BaseObject))
  64. || x.DeclaringType.IsSubclassOf(typeof(BaseEditor)))
  65. ); //.OrderBy(x=>x.Name);
  66. var classProps = _Properties.GetValueOrDefault(classname);
  67. foreach (var prop in properties)
  68. {
  69. var name = string.IsNullOrWhiteSpace(prefix) ? prop.Name : string.Format("{0}.{1}", prefix, prop.Name);
  70. var p = classProps?.GetValueOrDefault(name);
  71. //.ToArray().FirstOrDefault(x => x.Class == classname && x.Name == name);
  72. if (p == null)
  73. {
  74. var isstatic = prop.GetAccessors(true)[0].IsStatic;
  75. if (!isstatic)
  76. {
  77. BaseEditor? editor;
  78. if (parent != null && parent.HasEditor && parent.Editor is NullEditor)
  79. {
  80. editor = parent.Editor;
  81. }
  82. else
  83. {
  84. editor = prop.GetEditor();
  85. }
  86. var captionAttr = prop.GetCustomAttribute<Caption>();
  87. var subCaption = captionAttr != null ? captionAttr.Text : prop.Name;
  88. var path = captionAttr == null || captionAttr.IncludePath; // If no caption attribute, we should always include the path
  89. var caption = parent?.Caption ?? ""; // We default to the parent caption if subCaption doesn't exist
  90. if (!string.IsNullOrWhiteSpace(subCaption))
  91. {
  92. if (!string.IsNullOrWhiteSpace(caption) && path)
  93. {
  94. caption = $"{caption} {subCaption}";
  95. }
  96. else
  97. {
  98. caption = subCaption;
  99. }
  100. }
  101. // Once the parent page has been found, this property is cemented to that page - it cannot change page to its parent
  102. // The same goes for sequence
  103. var page = parent?.Page;
  104. var sequence = parent?.Sequence;
  105. if (string.IsNullOrWhiteSpace(page))
  106. {
  107. var sequenceAttribute = prop.GetCustomAttribute<EditorSequence>();
  108. if (sequenceAttribute != null)
  109. {
  110. page = sequenceAttribute.Page;
  111. sequence = sequenceAttribute.Sequence;
  112. }
  113. }
  114. editor = editor?.Clone() as BaseEditor;
  115. if (editor != null)
  116. {
  117. editor.Page = page;
  118. editor.Caption = caption;
  119. editor.EditorSequence = (int)(sequence ?? 999);
  120. }
  121. bool required = false;
  122. if (parent == null || parent.Required)
  123. {
  124. required = prop.GetCustomAttribute<RequiredColumnAttribute>() != null;
  125. }
  126. LoggablePropertyAttribute? loggable = null;
  127. if (parent == null || parent.Loggable != null)
  128. {
  129. loggable = prop.GetCustomAttribute<LoggablePropertyAttribute>();
  130. }
  131. var newProperty = new StandardProperty
  132. {
  133. _class = master,
  134. //Class = classname,
  135. Name = name,
  136. PropertyType = prop.PropertyType,
  137. Editor = editor ?? new NullEditor(),
  138. HasEditor = editor != null,
  139. Caption = caption,
  140. Sequence = sequence ?? 999,
  141. Page = page ?? "",
  142. Required = required,
  143. Loggable = loggable,
  144. Parent = parent,
  145. Property = prop
  146. };
  147. if (prop.PropertyType.GetInterfaces().Contains(typeof(IEntityLink)) ||
  148. prop.PropertyType.GetInterfaces().Contains(typeof(IEnclosedEntity)) ||
  149. prop.PropertyType.Equals(typeof(BaseEditor)) ||
  150. prop.PropertyType.IsSubclassOf(typeof(BaseEditor)))
  151. {
  152. if (prop.PropertyType.GetInterfaces().Contains(typeof(IEnclosedEntity)))
  153. {
  154. //RegisterProperty(newProperty);
  155. }
  156. RegisterProperties(master, prop.PropertyType, name, newProperty);
  157. }
  158. else
  159. {
  160. RegisterProperty(newProperty);
  161. }
  162. }
  163. }
  164. }
  165. if (type.IsSubclassOf(typeof(BaseObject)))
  166. //if ((type.BaseType != null) && (type.BaseType != typeof(BaseObject)))
  167. RegisterProperties(master, type.BaseType, prefix, parent);
  168. }
  169. catch (Exception e)
  170. {
  171. Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace));
  172. }
  173. }
  174. private static void RegisterProperties(Type type)
  175. {
  176. RegisterProperties(type, type, "", null);
  177. }
  178. public static object? DefaultValue(Type type)
  179. {
  180. if (type.IsValueType)
  181. return Activator.CreateInstance(type);
  182. if (type.Equals(typeof(string)))
  183. return "";
  184. return null;
  185. }
  186. private static readonly object _updatelock = new object();
  187. public static void RegisterProperty(IProperty entry)
  188. {
  189. lock (_updatelock)
  190. {
  191. if (!_Properties.ContainsKey(entry.Class))
  192. _Properties[entry.Class] = new Dictionary<string, IProperty>();
  193. _Properties[entry.Class][entry.Name] = entry;
  194. //var dict = _Properties.GetOrAdd(entry.Class, className => new Dictionary<string, IProperty>());
  195. //dict.TryAdd(entry.Name, entry);
  196. }
  197. }
  198. public static void Load(CustomProperty[] customproperties)
  199. {
  200. foreach (var prop in customproperties)
  201. RegisterProperty(prop);
  202. }
  203. private static void CheckProperties(Type type)
  204. {
  205. var entityName = type.EntityName();
  206. var props = _Properties.GetValueOrDefault(entityName);
  207. var hasprops = props?.Any(x => x.Value is StandardProperty) == true;
  208. if (type.IsSubclassOf(typeof(BaseObject)) && !hasprops)
  209. RegisterProperties(type);
  210. }
  211. public static IEnumerable<IProperty> Properties(Type type)
  212. {
  213. CheckProperties(type);
  214. var entityName = type.EntityName();
  215. return _Properties.GetValueOrDefault(entityName)?.Select(x => x.Value) ?? Array.Empty<IProperty>();
  216. }
  217. public static IProperty? Property(Type type, string name)
  218. {
  219. CheckProperties(type);
  220. var entityName = type.EntityName();
  221. //IProperty prop = _Properties.ToArray().FirstOrDefault(x => (x.Class.Equals(type.EntityName())) && (x.Name.Equals(name)));
  222. var prop = _Properties.GetValueOrDefault(entityName)?.GetValueOrDefault(name);
  223. // Walk up the inheritance tree, see if an ancestor has this property
  224. if (prop == null && type.BaseType != null)
  225. prop = Property(type.BaseType, name);
  226. return prop;
  227. }
  228. public static void InitializeObject<TObject>(TObject entity) where TObject : BaseObject
  229. {
  230. entity.UserProperties.Clear();
  231. var props = Properties(entity.GetType()).Where(x => x is CustomProperty).ToArray();
  232. foreach (var field in props) entity.UserProperties[field.Name] = DefaultValue(field.PropertyType);
  233. }
  234. }
  235. }