You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

429 lines
16 KiB

4 years ago
4 years ago
4 years ago
  1. using Apewer.Internals;
  2. using Apewer;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Data;
  6. using System.Reflection;
  7. using System.Text;
  8. namespace Apewer.Source
  9. {
  10. /// <summary></summary>
  11. [Serializable]
  12. public sealed class TableStructure
  13. {
  14. private string _tablename = Constant.EmptyString;
  15. private bool _independent = false;
  16. private Dictionary<string, ColumnAttribute> _columns = new Dictionary<string, ColumnAttribute>();
  17. internal TableStructure() { }
  18. /// <summary>不依赖 Record 公共属性。</summary>
  19. public bool Independent
  20. {
  21. get => _independent;
  22. private set => _independent = value;
  23. }
  24. /// <summary>表名称。</summary>
  25. public string Table
  26. {
  27. get => _tablename;
  28. private set => _tablename = value ?? "";
  29. }
  30. /// <summary>列信息。</summary>
  31. public Dictionary<string, ColumnAttribute> Columns
  32. {
  33. get => _columns;
  34. private set => _columns = value;
  35. }
  36. #region cache
  37. private static Dictionary<string, TableStructure> _tsc = new Dictionary<string, TableStructure>();
  38. private static Dictionary<string, TableAttribute> _tac = new Dictionary<string, TableAttribute>();
  39. #endregion
  40. #region static
  41. /// <summary></summary>
  42. /// <exception cref="System.Exception"></exception>
  43. /// <exception cref="System.ArgumentNullException"></exception>
  44. public static TableStructure ParseModel(object entity, bool useCache = true)
  45. {
  46. if (entity == null) throw new ArgumentNullException("参数无效");
  47. var type = entity.GetType();
  48. var result = ParseModel(type, useCache);
  49. return result;
  50. }
  51. /// <summary></summary>
  52. /// <exception cref="System.Exception"></exception>
  53. /// <exception cref="System.ArgumentNullException"></exception>
  54. public static TableStructure ParseModel<T>(bool useCache = true) where T : IRecord
  55. {
  56. return ParseModel(typeof(T), useCache);
  57. }
  58. /// <summary></summary>
  59. /// <exception cref="System.Exception"></exception>
  60. /// <exception cref="System.ArgumentNullException"></exception>
  61. public static TableStructure ParseModel(Type model, bool useCache = true)
  62. {
  63. var type = model;
  64. if (type == null) throw new ArgumentNullException("参数无效");
  65. // 使用缓存。
  66. var cacheKey = type.FullName;
  67. if (useCache)
  68. {
  69. var hint = null as TableStructure;
  70. lock (_tsc)
  71. {
  72. if (_tsc.ContainsKey(cacheKey))
  73. {
  74. hint = _tsc[cacheKey];
  75. }
  76. }
  77. if (hint != null) return hint;
  78. }
  79. // 检查基类。
  80. // if (type.BaseType.FullName.Equals(typeof(DatabaseRecord).FullName) == false) return "基类不受支持。";
  81. // 检查 Attribute。
  82. var ta = ParseTable(type);
  83. // 获取所有属性。
  84. var properties = type.GetProperties();
  85. if (properties.LongLength < 1L) throw new Exception(TextGenerator.Merge("类 ", type.FullName, " 不包含属性。"));
  86. // Record 根类属性名。
  87. var roots = GetRootProperties();
  88. // 检查字段定义。键:属性名称。
  89. var columns = new Dictionary<string, ColumnAttribute>();
  90. foreach (var property in properties)
  91. {
  92. var ca = ParseColumn(type, property);
  93. if (ca == null) continue;
  94. // 检查冗余。
  95. foreach (var column in columns)
  96. {
  97. if (column.Value.Field == ca.Field)
  98. {
  99. throw new Exception(TextGenerator.Merge("类 ", type.FullName, " 中,属性 ", property.Name, " 的列名称存在冗余。"));
  100. }
  101. }
  102. // 检查基类。
  103. if (roots.Contains(ca.Property.Name)) ca.Independent = true;
  104. columns.Add(property.Name, ca);
  105. }
  106. // if (columns.Count < 1) throw new Exception(TextGenerator.Merge("类 ", type.FullName, " 不包含可用的列。"));
  107. // 排序。
  108. columns = SortColumns(columns);
  109. // 返回结果。
  110. var ts = new TableStructure();
  111. ts.Table = ta.Name;
  112. ts.Independent = ta.Independent;
  113. ts.Columns = columns;
  114. // 加入缓存。
  115. if (useCache)
  116. {
  117. lock (_tsc)
  118. {
  119. if (!_tsc.ContainsKey(cacheKey))
  120. {
  121. _tsc.Add(cacheKey, ts);
  122. }
  123. }
  124. }
  125. return ts;
  126. }
  127. /// <summary></summary>
  128. /// <param name="type"></param>
  129. /// <exception cref="Exception"></exception>"
  130. public static TableAttribute ParseTable<T>(bool useCache = true) where T : IRecord
  131. {
  132. return ParseTable(typeof(T), useCache);
  133. }
  134. /// <summary></summary>
  135. /// <param name="type"></param>
  136. /// <exception cref="Exception"></exception>"
  137. public static TableAttribute ParseTable(Type type, bool useCache = true)
  138. {
  139. // 使用缓存。
  140. var cacheKey = type.FullName;
  141. if (useCache)
  142. {
  143. var hint = null as TableAttribute;
  144. lock (_tac)
  145. {
  146. if (_tac.ContainsKey(cacheKey))
  147. {
  148. hint = _tac[cacheKey];
  149. }
  150. }
  151. if (hint != null) return hint;
  152. }
  153. var tas = type.GetCustomAttributes(typeof(TableAttribute), false);
  154. if (tas.LongLength < 1L) throw new Exception(TextGenerator.Merge("类 ", type.FullName, " 不包含 ", typeof(TableAttribute).FullName, "。"));
  155. if (tas.LongLength > 1L) throw new Exception(TextGenerator.Merge("类 ", type.FullName, " 包含多个 ", typeof(TableAttribute).FullName, "。"));
  156. var ta = (TableAttribute)tas[0];
  157. if (TextVerifier.IsBlank(ta.Name))
  158. {
  159. ta = new TableAttribute("_" + type.Name);
  160. if (TextVerifier.IsBlank(ta.Name)) throw new Exception(TextGenerator.Merge("类 ", type.FullName, " 的表名称无效。"));
  161. }
  162. ta.Independent = RuntimeUtility.ContainsAttribute<IndependentAttribute>(type, true);
  163. // 加入缓存。
  164. if (useCache)
  165. {
  166. lock (_tac)
  167. {
  168. if (!_tac.ContainsKey(cacheKey))
  169. {
  170. _tac.Add(cacheKey, ta);
  171. }
  172. }
  173. }
  174. return ta;
  175. }
  176. /// <summary></summary>
  177. /// <exception cref="Exception">Exception</exception>"
  178. public static ColumnAttribute ParseColumn(Type type, PropertyInfo property)
  179. {
  180. // 检查 Attributes。
  181. var cas = property.GetCustomAttributes(typeof(ColumnAttribute), false);
  182. if (cas.LongLength < 1L) return null;
  183. if (cas.LongLength > 1L) throw new Exception(TextGenerator.Merge("类 ", type.FullName, " 中,属性 ", property.Name, " 包含多个 ", typeof(ColumnAttribute).FullName, "。"));
  184. var ca = (ColumnAttribute)cas[0];
  185. // 检查列名称。
  186. if (TextVerifier.IsBlank(ca.Field))
  187. {
  188. ca = new ColumnAttribute("_" + property.Name, ca.Type, ca.Length, true);
  189. if (TextVerifier.IsBlank(ca.Field)) throw new Exception(TextGenerator.Merge("类 ", type.FullName, "中,属性 ", property.Name, " 的列名称无效。"));
  190. }
  191. // 检查属性方法。
  192. var getter = property.GetGetMethod(false);
  193. var setter = property.GetSetMethod(false);
  194. if (getter == null) throw new Exception(TextGenerator.Merge("类 ", type.FullName, " 中,属性 ", property.Name, " 不支持获取。"));
  195. if (setter == null) throw new Exception(TextGenerator.Merge("类 ", type.FullName, " 中,属性 ", property.Name, " 不支持设置。"));
  196. // 类型兼容。
  197. var pt = property.PropertyType;
  198. if (pt.Equals(typeof(byte[]).FullName)) ca.Type = ColumnType.Binary;
  199. else if (pt.Equals(typeof(Byte))) ca.Type = ColumnType.Integer;
  200. else if (pt.Equals(typeof(SByte))) ca.Type = ColumnType.Integer;
  201. else if (pt.Equals(typeof(Int16))) ca.Type = ColumnType.Integer;
  202. else if (pt.Equals(typeof(UInt16))) ca.Type = ColumnType.Integer;
  203. else if (pt.Equals(typeof(Int32))) ca.Type = ColumnType.Integer;
  204. else if (pt.Equals(typeof(UInt32))) ca.Type = ColumnType.Integer;
  205. else if (pt.Equals(typeof(Int64))) ca.Type = ColumnType.Integer;
  206. else if (pt.Equals(typeof(Single))) ca.Type = ColumnType.Float;
  207. else if (pt.Equals(typeof(Double))) ca.Type = ColumnType.Float;
  208. else if (pt.Equals(typeof(Decimal))) ca.Type = ColumnType.Float;
  209. else if (pt.Equals(typeof(DateTime))) ca.Type = ColumnType.DateTime;
  210. else if (pt.Equals(typeof(String)))
  211. {
  212. switch (ca.Type)
  213. {
  214. case ColumnType.Binary:
  215. case ColumnType.Integer:
  216. case ColumnType.Float:
  217. case ColumnType.DateTime:
  218. //throw new Exception(TextGenerator.Merge("类 ", type.FullName, " 中,属性 ", property.Name, " 的类型不受支持。"));
  219. ca.Type = ColumnType.NText;
  220. break;
  221. }
  222. }
  223. else
  224. {
  225. ca.Type = ColumnType.NText;
  226. }
  227. ca.Property = property;
  228. return ca;
  229. }
  230. /// <summary>排序。</summary>
  231. public static Dictionary<string, ColumnAttribute> SortColumns(Dictionary<string, ColumnAttribute> columns)
  232. {
  233. // if (type.BaseType.FullName.Equals(typeof(Record).FullName)) // 仅当使用基类时排序。
  234. var sorted = new Dictionary<string, ColumnAttribute>();
  235. if (columns.ContainsKey("Created")) sorted.Add("Created", columns["Created"]);
  236. if (columns.ContainsKey("Updated")) sorted.Add("Updated", columns["Updated"]);
  237. if (columns.ContainsKey("Flag")) sorted.Add("Flag", columns["Flag"]);
  238. if (columns.ContainsKey("Remark")) sorted.Add("Remark", columns["Remark"]);
  239. if (columns.ContainsKey("Key")) sorted.Add("Key", columns["Key"]);
  240. foreach (var property in columns.Keys)
  241. {
  242. if (property == "Created") continue;
  243. if (property == "Updated") continue;
  244. if (property == "Flag") continue;
  245. if (property == "Remark") continue;
  246. if (property == "Key") continue;
  247. sorted.Add(property, columns[property]);
  248. }
  249. return sorted;
  250. }
  251. /// <summary>限定表名称/列名称。</summary>
  252. public static string RestrictName(string name, bool underline)
  253. {
  254. if (name == null || name == Constant.EmptyString) return Constant.EmptyString;
  255. var lower = name.ToLower();
  256. var available = TextGenerator.Merge("_", Constant.NumberCollection, Constant.LowerCollection);
  257. var sb = new StringBuilder();
  258. foreach (var c in lower)
  259. {
  260. if (available.IndexOf(c) >= 0) sb.Append(c);
  261. }
  262. lower = sb.ToString();
  263. if (underline && !lower.StartsWith("_")) lower = TextGenerator.Merge("_", lower);
  264. while (lower.Length > 2 && lower.StartsWith("__")) lower = lower.Substring(1);
  265. if (lower == "_" || lower == Constant.EmptyString) return Constant.EmptyString;
  266. if (lower.Length > 255) lower = lower.Substring(0, 255);
  267. return lower;
  268. }
  269. private static IDataParameter GenerateDataParameter(Record entity, ColumnAttribute attribute, CreateDataParameterCallback callback)
  270. {
  271. var property = attribute.Property;
  272. if (property == null) return null;
  273. var getter = property.GetGetMethod();
  274. if (getter == null) return null;
  275. var parameter = null as IDataParameter;
  276. var value = getter.Invoke(entity, null);
  277. //
  278. if (attribute.Type == ColumnType.Binary || attribute.Type == ColumnType.Integer || attribute.Type == ColumnType.Float)
  279. {
  280. var temp = value;
  281. if (property.PropertyType.FullName == typeof(Decimal).FullName)
  282. {
  283. temp = TextUtility.GetDouble(temp.ToString());
  284. }
  285. parameter = callback(new Parameter(attribute.Field, temp, attribute.Type, attribute.Length));
  286. }
  287. else if (attribute.Type == ColumnType.DateTime)
  288. {
  289. parameter = callback(new Parameter(attribute.Field, value, attribute.Type, 0));
  290. }
  291. else if (property.PropertyType.Equals(typeof(String)))
  292. {
  293. var text = value as string;
  294. if (text == null) text = "";
  295. if (attribute.Length > 0)
  296. {
  297. switch (attribute.Type)
  298. {
  299. case ColumnType.VarChar:
  300. case ColumnType.NVarChar:
  301. text = TextUtility.RestrictLength(text, attribute.Length);
  302. break;
  303. case ColumnType.VarChar255:
  304. case ColumnType.NVarChar255:
  305. text = TextUtility.RestrictLength(text, 255);
  306. break;
  307. }
  308. }
  309. parameter = callback(new Parameter(attribute.Field, text, attribute.Type, attribute.Length));
  310. }
  311. else
  312. {
  313. var text = (value == null) ? TextUtility.EmptyString : value.ToString();
  314. parameter = callback(new Parameter(attribute.Field, text, attribute.Type, attribute.Length));
  315. }
  316. return parameter;
  317. }
  318. /// <summary>生成 IDataParameter 列表,用于 Insert 或 Update。</summary>
  319. /// <exception cref="ArgumentNullException"></exception>
  320. public List<IDataParameter> CreateDataParameters(Record entity, CreateDataParameterCallback callback, params string[] excluded)
  321. {
  322. if (entity == null) throw new ArgumentNullException(nameof(entity));
  323. if (callback == null) throw new ArgumentNullException(nameof(excluded));
  324. entity.FixProperties();
  325. var list = new List<IDataParameter>();
  326. foreach (var column in Columns)
  327. {
  328. var attribute = column.Value;
  329. if (ParseTable(entity.GetType()).Independent && attribute.Independent) continue;
  330. var parameter = GenerateDataParameter(entity, attribute, callback);
  331. if (parameter == null) continue;
  332. var add = true;
  333. foreach (var exclude in excluded)
  334. {
  335. var lower = parameter.ParameterName.ToLower();
  336. if (lower == exclude.ToLower())
  337. {
  338. add = false;
  339. }
  340. }
  341. if (add) list.Add(parameter);
  342. }
  343. return list;
  344. }
  345. #endregion
  346. #region
  347. /// <summary>获取 Record 根类中的属性名称。</summary>
  348. private static List<string> GetRootProperties()
  349. {
  350. var list = new List<string>();
  351. var type = typeof(Record);
  352. var properties = type.GetProperties();
  353. foreach (var property in properties)
  354. {
  355. if (RuntimeUtility.ContainsAttribute<ColumnAttribute>(property, false))
  356. {
  357. list.Add(property.Name);
  358. }
  359. }
  360. return list;
  361. }
  362. #endregion
  363. }
  364. }