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.

338 lines
14 KiB

  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></summary>
  19. public bool Independent { get { return _independent; } private set { _independent = value; } }
  20. /// <summary></summary>
  21. public string Table { get { return _tablename; } set { _tablename = value ?? ""; } }
  22. /// <summary></summary>
  23. public Dictionary<string, ColumnAttribute> Columns { get { return _columns; } set { _columns = value; } }
  24. #region static
  25. /// <summary></summary>
  26. /// <exception cref="System.Exception"></exception>
  27. /// <exception cref="System.ArgumentNullException"></exception>
  28. public static TableStructure ParseModel(object entity)
  29. {
  30. if (entity == null) throw new ArgumentNullException("参数无效");
  31. var type = entity.GetType();
  32. var result = ParseModel(type);
  33. return result;
  34. }
  35. /// <summary></summary>
  36. /// <exception cref="System.Exception"></exception>
  37. /// <exception cref="System.ArgumentNullException"></exception>
  38. public static TableStructure ParseModel(Type model)
  39. {
  40. var type = model;
  41. if (type == null) throw new ArgumentNullException("参数无效");
  42. // 检查基类。
  43. // if (type.BaseType.FullName.Equals(typeof(DatabaseRecord).FullName) == false) return "基类不受支持。";
  44. // 检查 Attribute。
  45. var ta = ParseTable(type);
  46. // 获取所有属性。
  47. var properties = type.GetProperties();
  48. if (properties.LongLength < 1L) throw new Exception(TextGenerator.Merge("类 ", type.FullName, " 不包含属性。"));
  49. // Record 根类属性名。
  50. var roots = GetRootProperties();
  51. // 检查字段定义。键:属性名称。
  52. var columns = new Dictionary<string, ColumnAttribute>();
  53. foreach (var property in properties)
  54. {
  55. var ca = ParseColumn(type, property);
  56. if (ca == null) continue;
  57. // 检查冗余。
  58. foreach (var column in columns)
  59. {
  60. if (column.Value.Field == ca.Field)
  61. {
  62. throw new Exception(TextGenerator.Merge("类 ", type.FullName, " 中,属性 ", property.Name, " 的列名称存在冗余。"));
  63. }
  64. }
  65. // 检查基类。
  66. if (roots.Contains(ca.Property.Name)) ca.Independent = true;
  67. columns.Add(property.Name, ca);
  68. }
  69. // if (columns.Count < 1) throw new Exception(TextGenerator.Merge("类 ", type.FullName, " 不包含可用的列。"));
  70. // 排序。
  71. columns = SortColumns(columns);
  72. // 返回结果。
  73. var result = new TableStructure();
  74. result.Table = ta.Name;
  75. result.Independent = ta.Independent;
  76. result.Columns = columns;
  77. return result;
  78. }
  79. /// <summary></summary>
  80. /// <param name="type"></param>
  81. /// <exception cref="Exception"></exception>"
  82. public static TableAttribute ParseTable(Type type)
  83. {
  84. var tas = type.GetCustomAttributes(typeof(TableAttribute), false);
  85. if (tas.LongLength < 1L) throw new Exception(TextGenerator.Merge("类 ", type.FullName, " 不包含 ", typeof(TableAttribute).FullName, "。"));
  86. if (tas.LongLength > 1L) throw new Exception(TextGenerator.Merge("类 ", type.FullName, " 包含多个 ", typeof(TableAttribute).FullName, "。"));
  87. var ta = (TableAttribute)tas[0];
  88. if (TextVerifier.IsBlank(ta.Name))
  89. {
  90. ta = new TableAttribute("_" + type.Name);
  91. if (TextVerifier.IsBlank(ta.Name)) throw new Exception(TextGenerator.Merge("类 ", type.FullName, " 的表名称无效。"));
  92. }
  93. ta.Independent = ClassUtility.ContainsAttribute<IndependentAttribute>(type, true);
  94. return ta;
  95. }
  96. /// <summary></summary>
  97. /// <exception cref="Exception">Exception</exception>"
  98. public static ColumnAttribute ParseColumn(Type type, PropertyInfo property)
  99. {
  100. // 检查 Attributes。
  101. var cas = property.GetCustomAttributes(typeof(ColumnAttribute), false);
  102. if (cas.LongLength < 1L) return null;
  103. if (cas.LongLength > 1L) throw new Exception(TextGenerator.Merge("类 ", type.FullName, " 中,属性 ", property.Name, " 包含多个 ", typeof(ColumnAttribute).FullName, "。"));
  104. var ca = (ColumnAttribute)cas[0];
  105. // 检查列名称。
  106. if (TextVerifier.IsBlank(ca.Field))
  107. {
  108. ca = new ColumnAttribute("_" + property.Name, ca.Type, ca.Length, true);
  109. if (TextVerifier.IsBlank(ca.Field)) throw new Exception(TextGenerator.Merge("类 ", type.FullName, "中,属性 ", property.Name, " 的列名称无效。"));
  110. }
  111. // 检查属性方法。
  112. var getter = property.GetGetMethod(false);
  113. var setter = property.GetSetMethod(false);
  114. if (getter == null) throw new Exception(TextGenerator.Merge("类 ", type.FullName, " 中,属性 ", property.Name, " 不支持获取。"));
  115. if (setter == null) throw new Exception(TextGenerator.Merge("类 ", type.FullName, " 中,属性 ", property.Name, " 不支持设置。"));
  116. // 类型兼容。
  117. var pt = property.PropertyType;
  118. if (pt.Equals(typeof(byte[]).FullName)) ca.Type = ColumnType.Binary;
  119. else if (pt.Equals(typeof(Byte))) ca.Type = ColumnType.Integer;
  120. else if (pt.Equals(typeof(SByte))) ca.Type = ColumnType.Integer;
  121. else if (pt.Equals(typeof(Int16))) ca.Type = ColumnType.Integer;
  122. else if (pt.Equals(typeof(UInt16))) ca.Type = ColumnType.Integer;
  123. else if (pt.Equals(typeof(Int32))) ca.Type = ColumnType.Integer;
  124. else if (pt.Equals(typeof(UInt32))) ca.Type = ColumnType.Integer;
  125. else if (pt.Equals(typeof(Int64))) ca.Type = ColumnType.Integer;
  126. else if (pt.Equals(typeof(Single))) ca.Type = ColumnType.Float;
  127. else if (pt.Equals(typeof(Double))) ca.Type = ColumnType.Float;
  128. else if (pt.Equals(typeof(Decimal))) ca.Type = ColumnType.Float;
  129. else if (pt.Equals(typeof(DateTime))) ca.Type = ColumnType.DateTime;
  130. else if (pt.Equals(typeof(String)))
  131. {
  132. switch (ca.Type)
  133. {
  134. case ColumnType.Binary:
  135. case ColumnType.Integer:
  136. case ColumnType.Float:
  137. case ColumnType.DateTime:
  138. //throw new Exception(TextGenerator.Merge("类 ", type.FullName, " 中,属性 ", property.Name, " 的类型不受支持。"));
  139. ca.Type = ColumnType.NText;
  140. break;
  141. }
  142. }
  143. else
  144. {
  145. ca.Type = ColumnType.NText;
  146. }
  147. ca.Property = property;
  148. return ca;
  149. }
  150. /// <summary>排序。</summary>
  151. public static Dictionary<string, ColumnAttribute> SortColumns(Dictionary<string, ColumnAttribute> columns)
  152. {
  153. // if (type.BaseType.FullName.Equals(typeof(Record).FullName)) // 仅当使用基类时排序。
  154. var sorted = new Dictionary<string, ColumnAttribute>();
  155. if (columns.ContainsKey("Created")) sorted.Add("Created", columns["Created"]);
  156. if (columns.ContainsKey("Updated")) sorted.Add("Updated", columns["Updated"]);
  157. if (columns.ContainsKey("Flag")) sorted.Add("Flag", columns["Flag"]);
  158. if (columns.ContainsKey("Remark")) sorted.Add("Remark", columns["Remark"]);
  159. if (columns.ContainsKey("Key")) sorted.Add("Key", columns["Key"]);
  160. foreach (var property in columns.Keys)
  161. {
  162. if (property == "Created") continue;
  163. if (property == "Updated") continue;
  164. if (property == "Flag") continue;
  165. if (property == "Remark") continue;
  166. if (property == "Key") continue;
  167. sorted.Add(property, columns[property]);
  168. }
  169. return sorted;
  170. }
  171. /// <summary>限定表名称/列名称。</summary>
  172. public static string RestrictName(string name, bool underline)
  173. {
  174. if (name == null || name == Constant.EmptyString) return Constant.EmptyString;
  175. var lower = name.ToLower();
  176. var available = TextGenerator.Merge("_", Constant.NumberCollection, Constant.LowerCollection);
  177. var sb = new StringBuilder();
  178. foreach (var c in lower)
  179. {
  180. if (available.IndexOf(c) >= 0) sb.Append(c);
  181. }
  182. lower = sb.ToString();
  183. if (underline && !lower.StartsWith("_")) lower = TextGenerator.Merge("_", lower);
  184. while (lower.Length > 2 && lower.StartsWith("__")) lower = lower.Substring(1);
  185. if (lower == "_" || lower == Constant.EmptyString) return Constant.EmptyString;
  186. if (lower.Length > 255) lower = lower.Substring(0, 255);
  187. return lower;
  188. }
  189. private static IDataParameter GenerateDataParameter(Record entity, ColumnAttribute attribute, CreateDataParameterCallback callback)
  190. {
  191. var property = attribute.Property;
  192. if (property == null) return null;
  193. var getter = property.GetGetMethod();
  194. if (getter == null) return null;
  195. var parameter = null as IDataParameter;
  196. var value = getter.Invoke(entity, null);
  197. //
  198. if (attribute.Type == ColumnType.Binary || attribute.Type == ColumnType.Integer || attribute.Type == ColumnType.Float)
  199. {
  200. var temp = value;
  201. if (property.PropertyType.FullName == typeof(Decimal).FullName)
  202. {
  203. temp = TextUtility.GetDouble(temp.ToString());
  204. }
  205. parameter = callback(new Parameter(attribute.Field, temp, attribute.Type, attribute.Length));
  206. }
  207. else if (attribute.Type == ColumnType.DateTime)
  208. {
  209. parameter = callback(new Parameter(attribute.Field, value, attribute.Type, 0));
  210. }
  211. else if (property.PropertyType.Equals(typeof(String)))
  212. {
  213. var text = value as string;
  214. if (text == null) text = "";
  215. if (attribute.Length > 0)
  216. {
  217. switch (attribute.Type)
  218. {
  219. case ColumnType.VarChar:
  220. case ColumnType.NVarChar:
  221. text = TextUtility.RestrictLength(text, attribute.Length);
  222. break;
  223. case ColumnType.VarChar255:
  224. case ColumnType.NVarChar255:
  225. text = TextUtility.RestrictLength(text, 255);
  226. break;
  227. }
  228. }
  229. parameter = callback(new Parameter(attribute.Field, text, attribute.Type, attribute.Length));
  230. }
  231. else
  232. {
  233. var text = (value == null) ? TextUtility.EmptyString : value.ToString();
  234. parameter = callback(new Parameter(attribute.Field, text, attribute.Type, attribute.Length));
  235. }
  236. return parameter;
  237. }
  238. /// <summary>生成 IDataParameter 列表,用于 Insert 或 Update。</summary>
  239. /// <exception cref="ArgumentNullException"></exception>
  240. public List<IDataParameter> CreateDataParameters(Record entity, CreateDataParameterCallback callback, params string[] excluded)
  241. {
  242. if (entity == null) throw new ArgumentNullException("argEntity");
  243. if (callback == null) throw new ArgumentNullException("argCallback");
  244. entity.FixProperties();
  245. var list = new List<IDataParameter>();
  246. foreach (var column in Columns)
  247. {
  248. var attribute = column.Value;
  249. if (ParseTable(entity.GetType()).Independent && attribute.Independent) continue;
  250. var parameter = GenerateDataParameter(entity, attribute, callback);
  251. if (parameter == null) continue;
  252. var add = true;
  253. foreach (var exclude in excluded)
  254. {
  255. var lower = parameter.ParameterName.ToLower();
  256. if (lower == exclude.ToLower())
  257. {
  258. add = false;
  259. }
  260. }
  261. if (add) list.Add(parameter);
  262. }
  263. return list;
  264. }
  265. #endregion
  266. #region
  267. /// <summary>获取 Record 根类中的属性名称。</summary>
  268. private static List<string> GetRootProperties()
  269. {
  270. var list = new List<string>();
  271. var type = typeof(Record);
  272. var properties = type.GetProperties();
  273. foreach (var property in properties)
  274. {
  275. if (ClassUtility.ContainsAttribute<ColumnAttribute>(property, false))
  276. {
  277. list.Add(property.Name);
  278. }
  279. }
  280. return list;
  281. }
  282. #endregion
  283. }
  284. }