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.

374 lines
14 KiB

4 years ago
4 years ago
3 years ago
4 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
4 years ago
3 years ago
4 years ago
4 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
4 years ago
4 years ago
3 years ago
4 years ago
4 years ago
3 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 System;
  2. using System.Collections.Generic;
  3. using System.Data;
  4. using System.Reflection;
  5. namespace Apewer.Source
  6. {
  7. /// <summary>表结构。</summary>
  8. [Serializable]
  9. public sealed class TableStructure
  10. {
  11. #region Instance
  12. Type _model = null;
  13. TableAttribute _table = null;
  14. ColumnAttribute _key = null;
  15. ColumnAttribute _flag = null;
  16. TableIndex[] _indexes = null;
  17. ColumnAttribute[] _columns = null;
  18. ColumnAttribute[] _fillable = null;
  19. private TableStructure() { }
  20. /// <summary>使用此结构的记录模型。</summary>
  21. public Type Model { get => _model; }
  22. /// <summary>表特性。</summary>
  23. public TableAttribute Table { get => _table; }
  24. /// <summary>索引。</summary>
  25. public TableIndex[] Indexes { get => _indexes; }
  26. /// <summary>列信息。</summary>
  27. public ColumnAttribute[] Columns { get => _columns; }
  28. /// <summary>可填充的列信息。</summary>
  29. public ColumnAttribute[] Fillable { get => _fillable; }
  30. /// <summary>主键。</summary>
  31. public ColumnAttribute Key { get => _key; }
  32. /// <summary>记录标记。</summary>
  33. public ColumnAttribute Flag { get => _flag; }
  34. /// <summary>从 <see cref="TableStructure"/> 到 Boolean 的隐式转换,判断 <see cref="TableStructure"/> 有效。</summary>
  35. /// <remarks>True:存在有效结构时;<br />False:不存在有效结构。</remarks>
  36. public static implicit operator bool(TableStructure instance)
  37. {
  38. if (instance == null) return false;
  39. if (instance._model == null) return false;
  40. if (instance._columns == null) return false;
  41. return true;
  42. }
  43. #endregion
  44. #region Obsolete
  45. /// <summary>不依赖 Record 公共属性。</summary>
  46. /// <remarks>注意:此属性即将弃用,应改为使用 Table.Independent 属性。</remarks>
  47. public bool Independent { get => _table == null ? false : _table.Independent; }
  48. /// <summary>表的说明信息。</summary>
  49. /// <remarks>注意:此属性即将弃用,应改为使用 Table.Description 属性。</remarks>
  50. public string Description { get => _table == null ? null : _table.Description; }
  51. /// <summary>表特性。</summary>
  52. /// <remarks>注意:此属性即将弃用,应改为使用 Table 属性。</remarks>
  53. public TableAttribute Attribute { get => _table; }
  54. /// <summary>使用模型的所有属性,对缺少 Column 特性的属性使用默认参数的 Column 特性。</summary>
  55. /// <remarks>注意:此属性即将弃用,应改为使用 Table.AllProperties 属性。</remarks>
  56. public bool AllProperties { get => _table == null ? false : _table.AllProperties; }
  57. /// <summary>表名。</summary>
  58. /// <remarks>注意:此属性即将弃用,应改为使用 Table.Name 属性。</remarks>
  59. public string TableName { get => _table == null ? null : _table.Name; }
  60. #endregion
  61. #region TableStructure
  62. private static Dictionary<string, TableStructure> _tsc = new Dictionary<string, TableStructure>();
  63. /// <summary>解析表结构。</summary>
  64. public static TableStructure Parse<T>(bool useCache = true, bool force = false) where T : IRecord => Parse(typeof(T), useCache, force);
  65. /// <summary>解析表结构。</summary>
  66. /// <returns>表结构。类型不可用于表结构时返回 NULL 值。</returns>
  67. public static TableStructure Parse(Type model, bool useCache = true, bool force = false)
  68. {
  69. var type = model;
  70. if (type == null || !type.IsClass || type.IsAbstract) return null;
  71. // 使用缓存。
  72. var cacheKey = type.FullName;
  73. if (force) cacheKey = "[force] " + cacheKey;
  74. if (useCache)
  75. {
  76. lock (_tsc)
  77. {
  78. TableStructure cached;
  79. if (_tsc.TryGetValue(cacheKey, out cached)) return cached;
  80. }
  81. }
  82. // 解析 TableAttribute。
  83. var ta = TableAttribute.Parse(type, useCache, force);
  84. if (!ta && !force) return null;
  85. // 类型。
  86. var isRecord = RuntimeUtility.IsInherits(type, typeof(Record));
  87. var properties = GetProperties(type);
  88. var total = properties.Length;
  89. // 解析 ColumnAttribute。
  90. var key = null as ColumnAttribute;
  91. var flag = null as ColumnAttribute;
  92. var columns = new ColumnAttribute[total];
  93. var columnsCount = 0;
  94. var fillable = new List<ColumnAttribute>(total);
  95. var indexesDict = new Dictionary<string, List<IndexAttribute>>();
  96. if (total > 0)
  97. {
  98. var caForce = force || (ta ? ta.AllProperties : false);
  99. var addedFields = new List<string>(total);
  100. foreach (var property in properties)
  101. {
  102. var ca = ColumnAttribute.Parse(property, caForce);
  103. if (ca != null)
  104. {
  105. // 检查 field 重复,只保留第一个。
  106. var field = ca.Field;
  107. if (!addedFields.Contains(field))
  108. {
  109. addedFields.Add(field);
  110. columns[columnsCount] = ca;
  111. columnsCount += 1;
  112. if (isRecord)
  113. {
  114. if (property.Name == "Key")
  115. {
  116. key = ca;
  117. if (ta != null && ta.PrimaryKey) ca.SetPrimaryKey();
  118. }
  119. else if (property.Name == "Flag")
  120. {
  121. flag = ca;
  122. }
  123. }
  124. }
  125. // 索引
  126. foreach (var index in ca.Indexes)
  127. {
  128. var indexName = index.Name.Lower();
  129. if (indexName.IsEmpty()) continue;
  130. if (indexesDict.TryGetValue(indexName, out var list))
  131. {
  132. list.Add(index);
  133. }
  134. else
  135. {
  136. list = new List<IndexAttribute>();
  137. list.Add(index);
  138. indexesDict.Add(indexName, list);
  139. }
  140. }
  141. // 可查询的列。
  142. fillable.Add(ca);
  143. continue;
  144. }
  145. // 可查询的列。
  146. if (!caForce)
  147. {
  148. ca = ColumnAttribute.Parse(property, caForce);
  149. if (ca) fillable.Add(ca);
  150. }
  151. }
  152. }
  153. if (columnsCount > 0 && columnsCount != columns.Length) columns = columns.Slice(0, columnsCount);
  154. // 整理索引。
  155. var indexes = new List<TableIndex>();
  156. foreach (var indexesList in indexesDict.Values)
  157. {
  158. var indexName = indexesList[0].Name;
  159. var tableIndex = new TableIndex(indexName, ta, indexesList.ToArray());
  160. indexes.Add(tableIndex);
  161. }
  162. // 排序,将 Key 和 Flag 排在最前。
  163. columns = ColumnAttribute.Sort(columns);
  164. // 返回结果。
  165. var ts = new TableStructure();
  166. ts._table = ta;
  167. ts._key = key;
  168. ts._flag = flag;
  169. ts._indexes = indexes.ToArray();
  170. ts._columns = columns;
  171. ts._model = model;
  172. ts._fillable = fillable.ToArray();
  173. // 加入缓存。
  174. if (useCache)
  175. {
  176. lock (_tsc)
  177. {
  178. if (!_tsc.ContainsKey(cacheKey)) _tsc.Add(cacheKey, ts);
  179. }
  180. }
  181. return ts;
  182. }
  183. // 获取属性,基类的属性排在前面。
  184. static PropertyInfo[] GetProperties(Type type)
  185. {
  186. if (type == null) throw new ArgumentNullException(nameof(type));
  187. // 获取所有属性的名称,基类在前。
  188. var chian = new List<Type>();
  189. var current = type;
  190. while (true)
  191. {
  192. if (current.Equals(RuntimeUtility.ObjectType)) break;
  193. chian.Add(current);
  194. current = current.BaseType;
  195. }
  196. var names = new List<string>();
  197. for (var i = chian.Count - 1; i >= 0; i--)
  198. {
  199. var declared = chian[i].GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
  200. foreach (var property in declared)
  201. {
  202. if (names.Contains(property.Name))
  203. {
  204. continue;
  205. }
  206. names.Add(property.Name);
  207. }
  208. }
  209. // 获取所有属性。
  210. var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
  211. var result = names.Map(n => properties.Find(p => p.Name == n));
  212. result = result.FindAll(p => p != null);
  213. return result;
  214. }
  215. #endregion
  216. #region TableAttribute
  217. // 限定表名称/列名称。
  218. static string RestrictName(string name, bool underline = false, bool english = false)
  219. {
  220. if (string.IsNullOrEmpty(name)) return null;
  221. var str = name;
  222. // 限定名称仅使用英文和数字。
  223. if (english)
  224. {
  225. const string available = "_0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
  226. var chars = new ArrayBuilder<char>();
  227. var strChars = str.ToCharArray();
  228. var strLength = strChars.Length > 255 ? 255 : strChars.Length;
  229. for (var i = 0; i < strLength; i++)
  230. {
  231. if (available.IndexOf(strChars[i]) > -1) chars.Add(strChars[i]);
  232. }
  233. str = new string(chars.Export());
  234. }
  235. // 以下划线开始。
  236. if (underline)
  237. {
  238. if (!str.StartsWith("_")) str = TextUtility.Merge("_", str);
  239. }
  240. return str;
  241. }
  242. static IDataParameter CreateParameter(object record, ColumnAttribute ca, Func<Parameter, IDataParameter> callback)
  243. {
  244. var property = ca.Property;
  245. if (property == null) return null;
  246. var getter = property.GetGetMethod();
  247. if (getter == null) return null;
  248. var value = getter.Invoke(record, null);
  249. if (ca.Type == ColumnType.Bytes || ca.Type == ColumnType.Integer || ca.Type == ColumnType.Float)
  250. {
  251. return callback(new Parameter(ca.Field, value, ca.Type, ca.Length));
  252. }
  253. if (ca.Type == ColumnType.DateTime)
  254. {
  255. return callback(new Parameter(ca.Field, value, ca.Type, 0));
  256. }
  257. if (property.PropertyType.Equals(typeof(String)))
  258. {
  259. var text = value as string;
  260. if (text == null) text = "";
  261. if (ca.Length > 0)
  262. {
  263. switch (ca.Type)
  264. {
  265. case ColumnType.VarChar:
  266. case ColumnType.NVarChar:
  267. text = TextUtility.Left(text, ca.Length);
  268. break;
  269. case ColumnType.VarChar191:
  270. case ColumnType.NVarChar191:
  271. text = TextUtility.Left(text, 191);
  272. break;
  273. }
  274. }
  275. return callback(new Parameter(ca.Field, text, ca.Type, ca.Length));
  276. }
  277. var defaultText = (value == null) ? TextUtility.Empty : value.ToString();
  278. return callback(new Parameter(ca.Field, defaultText, ca.Type, ca.Length));
  279. }
  280. /// <summary>生成 IDataParameter 列表,用于 Insert 和 Update 方法。</summary>
  281. public IDataParameter[] CreateParameters(object record, Func<Parameter, IDataParameter> callback, IEnumerable<string> excludeds)
  282. {
  283. if (record == null || callback == null) return null;
  284. var list = new List<IDataParameter>(_columns.Length);
  285. foreach (var ca in Columns)
  286. {
  287. if (ca == null) continue;
  288. var parameter = CreateParameter(record, ca, callback);
  289. if (parameter == null) continue;
  290. var add = true;
  291. if (excludeds != null)
  292. {
  293. var lower = parameter.ParameterName.ToLower();
  294. foreach (var excluded in excludeds)
  295. {
  296. if (string.IsNullOrEmpty(excluded)) continue;
  297. if (lower == excluded.ToLower())
  298. {
  299. add = false;
  300. break;
  301. }
  302. }
  303. }
  304. if (add) list.Add(parameter);
  305. }
  306. return list.ToArray();
  307. }
  308. #endregion
  309. }
  310. }