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.

258 lines
8.6 KiB

4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
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. #region Instance
  15. TableAttribute _attribute = null;
  16. bool _independent = false;
  17. string _name = null;
  18. string _description = null;
  19. bool _allprops = false;
  20. ColumnAttribute _key = null;
  21. ColumnAttribute _flag = null;
  22. ColumnAttribute[] _columns = null;
  23. Type _model = null;
  24. private TableStructure() { }
  25. /// <summary>不依赖 Record 公共属性。</summary>
  26. public bool Independent { get => _independent; }
  27. /// <summary>表名称。</summary>
  28. public string Name { get => _name; }
  29. /// <summary>表的说明信息。</summary>
  30. public string Description { get => _description; }
  31. /// <summary>使用模型的所有属性,自动对属性添加缺少的 Column 特性。</summary>
  32. public bool AllProperties { get => _allprops; }
  33. /// <summary>此结构的特性。</summary>
  34. public TableAttribute Attribute { get => _attribute; }
  35. /// <summary>使用此结构的记录模型。</summary>
  36. public Type Model { get => _model; }
  37. /// <summary>主键。</summary>
  38. public ColumnAttribute Key { get => _key; }
  39. /// <summary>记录标记。</summary>
  40. public ColumnAttribute Flag { get => _flag; }
  41. /// <summary>列信息。</summary>
  42. public ColumnAttribute[] Columns { get => _columns; }
  43. #endregion
  44. #region TableStructure
  45. private static Dictionary<string, TableStructure> _tsc = new Dictionary<string, TableStructure>();
  46. /// <summary>解析表结构。</summary>
  47. public static TableStructure Parse<T>(bool useCache = true) where T : IRecord => Parse(typeof(T), useCache);
  48. /// <summary>解析表结构。</summary>
  49. public static TableStructure Parse(Type model, bool useCache = true)
  50. {
  51. var type = model;
  52. if (type == null || !type.IsClass || type.IsAbstract) return null;
  53. // 使用缓存。
  54. var cacheKey = type.FullName;
  55. if (useCache)
  56. {
  57. lock (_tsc)
  58. {
  59. TableStructure cached;
  60. if (_tsc.TryGetValue(cacheKey, out cached)) return cached;
  61. }
  62. }
  63. // 获取 Table Attribute。
  64. var ta = TableAttribute.Parse(type);
  65. // 遍历所有属性。
  66. var properties = type.GetProperties();
  67. var key = null as ColumnAttribute;
  68. var flag = null as ColumnAttribute;
  69. var columns = new ColumnAttribute[properties.Length];
  70. var columnsCount = 0;
  71. if (properties.Length > 0)
  72. {
  73. var addedFields = new List<string>(properties.Length);
  74. foreach (var property in properties)
  75. {
  76. // 解析 ColumnAttribute,抛弃无效。
  77. var ca = ColumnAttribute.Parse(type, property, ta);
  78. if (ca == null) continue;
  79. // 检查 field 重复,只保留第一个。
  80. var field = ca.Field;
  81. if (addedFields.Contains(field)) continue;
  82. addedFields.Add(field);
  83. if (property.Name == "Key") key = ca;
  84. if (property.Name == "Flag") flag = ca;
  85. columns[columnsCount] = ca;
  86. columnsCount += 1;
  87. }
  88. }
  89. if (columnsCount > 0 && columnsCount != columns.Length) columns = columns.Slice(0, columnsCount);
  90. // 排序,将 Key 和 Flag 排在最前。
  91. columns = ColumnAttribute.Sort(columns);
  92. // 返回结果。
  93. var ts = new TableStructure();
  94. ts._attribute = ta;
  95. ts._key = key;
  96. ts._flag = flag;
  97. ts._name = ta.Name;
  98. ts._description = ta.Description;
  99. ts._allprops = ta.AllProperties;
  100. ts._independent = ta.Independent;
  101. ts._columns = columns;
  102. ts._model = model;
  103. // 加入缓存。
  104. if (useCache)
  105. {
  106. lock (_tsc)
  107. {
  108. if (!_tsc.ContainsKey(cacheKey)) _tsc.Add(cacheKey, ts);
  109. }
  110. }
  111. return ts;
  112. }
  113. #endregion
  114. #region TableAttribute
  115. // 限定表名称/列名称。
  116. static string RestrictName(string name, bool underline = false, bool english = false)
  117. {
  118. if (string.IsNullOrEmpty(name)) return null;
  119. var str = name;
  120. // 限定名称仅使用英文和数字。
  121. if (english)
  122. {
  123. const string available = "_0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
  124. var chars = new ArrayBuilder<char>();
  125. var strChars = str.ToCharArray();
  126. var strLength = strChars.Length > 255 ? 255 : strChars.Length;
  127. for (var i = 0; i < strLength; i++)
  128. {
  129. if (available.IndexOf(strChars[i]) > -1) chars.Add(strChars[i]);
  130. }
  131. str = new string(chars.Export());
  132. }
  133. // 以下划线开始。
  134. if (underline)
  135. {
  136. if (!str.StartsWith("_")) str = TextUtility.Merge("_", str);
  137. }
  138. return str;
  139. }
  140. static IDataParameter CreateParameter(object record, ColumnAttribute ca, Func<Parameter, IDataParameter> callback)
  141. {
  142. var property = ca.Property;
  143. if (property == null) return null;
  144. var getter = property.GetGetMethod();
  145. if (getter == null) return null;
  146. var value = getter.Invoke(record, null);
  147. if (ca.Type == ColumnType.Bytes || ca.Type == ColumnType.Integer || ca.Type == ColumnType.Float)
  148. {
  149. return callback(new Parameter(ca.Field, value, ca.Type, ca.Length));
  150. }
  151. if (ca.Type == ColumnType.DateTime)
  152. {
  153. return callback(new Parameter(ca.Field, value, ca.Type, 0));
  154. }
  155. if (property.PropertyType.Equals(typeof(String)))
  156. {
  157. var text = value as string;
  158. if (text == null) text = "";
  159. if (ca.Length > 0)
  160. {
  161. switch (ca.Type)
  162. {
  163. case ColumnType.VarChar:
  164. case ColumnType.NVarChar:
  165. text = TextUtility.Left(text, ca.Length);
  166. break;
  167. case ColumnType.VarChar191:
  168. case ColumnType.NVarChar191:
  169. text = TextUtility.Left(text, 191);
  170. break;
  171. }
  172. }
  173. return callback(new Parameter(ca.Field, text, ca.Type, ca.Length));
  174. }
  175. var defaultText = (value == null) ? TextUtility.Empty : value.ToString();
  176. return callback(new Parameter(ca.Field, defaultText, ca.Type, ca.Length));
  177. }
  178. /// <summary>生成 IDataParameter 列表,用于 Insert 和 Update 方法。</summary>
  179. public IDataParameter[] CreateParameters(object record, Func<Parameter, IDataParameter> callback, params string[] excludeds)
  180. {
  181. if (record == null || callback == null) return null;
  182. var list = new List<IDataParameter>(_columns.Length);
  183. foreach (var ca in Columns)
  184. {
  185. if (ca == null) continue;
  186. var parameter = CreateParameter(record, ca, callback);
  187. if (parameter == null) continue;
  188. var add = true;
  189. if (excludeds != null)
  190. {
  191. var lower = parameter.ParameterName.ToLower();
  192. foreach (var excluded in excludeds)
  193. {
  194. if (string.IsNullOrEmpty(excluded)) continue;
  195. if (lower == excluded.ToLower())
  196. {
  197. add = false;
  198. break;
  199. }
  200. }
  201. }
  202. if (add) list.Add(parameter);
  203. }
  204. return list.ToArray();
  205. }
  206. #endregion
  207. }
  208. }