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.

419 lines
18 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
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
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 System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. using static Apewer.NumberUtility;
  5. namespace Apewer.Source
  6. {
  7. /// <summary>ORM 帮助程序。</summary>
  8. public static class OrmHelper
  9. {
  10. #region As
  11. /// <summary>转换模型类型。</summary>
  12. public static TDst[] As<TSrc, TDst>(this TSrc[] input) where TDst : class
  13. {
  14. if (input == null) return null;
  15. var count = input.Length;
  16. var output = new TDst[count];
  17. for (var i = 0; i < count; i++)
  18. {
  19. var item = input[i];
  20. if (item == null) continue;
  21. output[i] = item as TDst; // 此处可能抛出异常。
  22. }
  23. return output;
  24. }
  25. /// <summary>转换模型类型。</summary>
  26. public static Result<TDst> As<TSrc, TDst>(this Result<TSrc> input) where TDst : class
  27. {
  28. if (input == null) return null;
  29. if (!input.HasValue) return new Result<TDst>(input.Code, input.Message);
  30. var value = input.Value as TDst;
  31. if (value == null)
  32. {
  33. var src = input.Value.GetType().FullName;
  34. var dst = typeof(TDst).FullName;
  35. return new Result<TDst>($"无法将记录从转换 {src} 到 {dst}。");
  36. }
  37. return new Result<TDst>(value);
  38. }
  39. /// <summary>转换模型类型。</summary>
  40. public static Result<TDst[]> As<TSrc, TDst>(this Result<TSrc[]> input) where TDst : class
  41. {
  42. if (input == null) return null;
  43. if (!input.HasValue) return new Result<TDst[]>(input.Code, input.Message);
  44. var count = input.Value.Length;
  45. var output = new TDst[count];
  46. for (var i = 0; i < count; i++) output[i] = input.Value[i] as TDst;
  47. return new Result<TDst[]>(output);
  48. }
  49. #endregion
  50. #region IQuery -> IRecord
  51. /// <summary>读取所有行,生成列表。</summary>
  52. public static T[] Fill<T>(this IQuery query) where T : class, new() => As<object, T>(Fill(query, typeof(T)));
  53. /// <summary>读取所有行填充到 T,组成 T[]。</summary>
  54. public static object[] Fill(this IQuery query, Type model)
  55. {
  56. if (query == null) return new object[0];
  57. if (model == null) return new object[0];
  58. var ts = TableStructure.Parse(model);
  59. if (ts == null) return new object[0];
  60. var output = new object[query.Rows];
  61. for (int r = 0; r < query.Rows; r++) output[r] = Row(query, r, model, ts);
  62. return output;
  63. }
  64. /// <summary>获取指定列的所有值,无效值不加入结果。</summary>
  65. public static T[] Column<T>(this IQuery query, Func<int, T> filler)
  66. {
  67. if (query == null || filler == null) return new T[0];
  68. var rows = query.Rows;
  69. var output = new T[rows];
  70. var added = 0;
  71. for (int r = 0; r < rows; r++)
  72. {
  73. var value = filler(r);
  74. if (value == null) continue;
  75. if (value is string str)
  76. {
  77. if (str == "") continue;
  78. }
  79. output[added] = value;
  80. added++;
  81. }
  82. if (added < 1) return new T[0];
  83. if (added == rows) return output;
  84. var output2 = new T[added];
  85. Array.Copy(output, output2, added);
  86. return output2;
  87. }
  88. /// <summary>将 Query 的行,填充到模型实体。</summary>
  89. /// <remarks>填充失败时返回 NULL 值。</remarks>
  90. /// <exception cref="Exception"></exception>
  91. public static object Row(IQuery query, int rowIndex, Type model, TableStructure structure)
  92. {
  93. // 检查参数。
  94. if (query == null || model == null || structure == null) return null;
  95. if (rowIndex < 0 || rowIndex >= query.Rows) return null;
  96. if (!RuntimeUtility.CanNew(model)) return null;
  97. // 变量别名。
  98. var ts = structure;
  99. var r = rowIndex;
  100. var columns = ts.Columns;
  101. // 检查模型的属性,按属性从表中取相应的列。
  102. var record = Activator.CreateInstance(model);
  103. var properties = model.GetProperties();
  104. foreach (var property in properties)
  105. {
  106. // 必须有 setter 访问器。
  107. var setter = property.GetSetMethod();
  108. if (setter == null) continue;
  109. // 在表结构中检查,是否包含此属性,并获取 ColumnAttribute。
  110. var attribute = null as ColumnAttribute;
  111. for (var j = 0; j < columns.Length; j++)
  112. {
  113. if (columns[j].PropertyName == property.Name)
  114. {
  115. attribute = columns[j];
  116. break;
  117. }
  118. }
  119. if (attribute == null) continue;
  120. // 读取值。
  121. var value = query.Value(r, attribute.Field);
  122. if (value == null) continue;
  123. if (value.Equals(DBNull.Value)) continue;
  124. // 根据属性类型设置值。
  125. var pt = property.PropertyType;
  126. if (pt.Equals(typeof(object))) setter.Invoke(record, new object[] { value });
  127. else if (pt.Equals(typeof(byte[]))) setter.Invoke(record, new object[] { (byte[])value });
  128. else if (pt.Equals(typeof(string))) setter.Invoke(record, new object[] { value.ToString() });
  129. else if (pt.Equals(typeof(DateTime))) setter.Invoke(record, new object[] { value });
  130. else if (pt.Equals(typeof(byte))) setter.Invoke(record, new object[] { Byte(value) });
  131. else if (pt.Equals(typeof(sbyte))) setter.Invoke(record, new object[] { SByte(value) });
  132. else if (pt.Equals(typeof(short))) setter.Invoke(record, new object[] { Int16(value) });
  133. else if (pt.Equals(typeof(ushort))) setter.Invoke(record, new object[] { UInt16(value) });
  134. else if (pt.Equals(typeof(int))) setter.Invoke(record, new object[] { Int32(value) });
  135. else if (pt.Equals(typeof(uint))) setter.Invoke(record, new object[] { UInt32(value) });
  136. else if (pt.Equals(typeof(long))) setter.Invoke(record, new object[] { Int64(value) });
  137. else if (pt.Equals(typeof(ulong))) setter.Invoke(record, new object[] { UInt64(value) });
  138. else if (pt.Equals(typeof(float))) setter.Invoke(record, new object[] { Single(value) });
  139. else if (pt.Equals(typeof(double))) setter.Invoke(record, new object[] { Double(value) });
  140. else if (pt.Equals(typeof(decimal))) setter.Invoke(record, new object[] { Decimal(value) });
  141. else if (pt.Equals(typeof(Nullable<DateTime>))) setter.Invoke(record, new object[] { new Nullable<DateTime>((DateTime)value) });
  142. else if (pt.Equals(typeof(Nullable<byte>))) setter.Invoke(record, new object[] { new Nullable<byte>(Byte(value) ) });
  143. else if (pt.Equals(typeof(Nullable<sbyte>))) setter.Invoke(record, new object[] { new Nullable<sbyte>(SByte(value) ) });
  144. else if (pt.Equals(typeof(Nullable<short>))) setter.Invoke(record, new object[] { new Nullable<short>(Int16(value) ) });
  145. else if (pt.Equals(typeof(Nullable<ushort>))) setter.Invoke(record, new object[] { new Nullable<int>(UInt16(value) ) });
  146. else if (pt.Equals(typeof(Nullable<int>))) setter.Invoke(record, new object[] { new Nullable<int>(Int32(value) ) });
  147. else if (pt.Equals(typeof(Nullable<uint>))) setter.Invoke(record, new object[] { new Nullable<uint>(UInt32(value) ) });
  148. else if (pt.Equals(typeof(Nullable<long>))) setter.Invoke(record, new object[] { new Nullable<long>(Int64(value) ) });
  149. else if (pt.Equals(typeof(Nullable<ulong>))) setter.Invoke(record, new object[] { new Nullable<ulong>(UInt64(value) ) });
  150. else if (pt.Equals(typeof(Nullable<float>))) setter.Invoke(record, new object[] { new Nullable<float>(Single(value) ) });
  151. else if (pt.Equals(typeof(Nullable<double>))) setter.Invoke(record, new object[] { new Nullable<double>(Double(value) ) });
  152. else if (pt.Equals(typeof(Nullable<decimal>))) setter.Invoke(record, new object[] { new Nullable<decimal>(Decimal(value) ) });
  153. else
  154. {
  155. try
  156. {
  157. setter.Invoke(record, new object[] { query.Value(r, attribute.Field) });
  158. }
  159. catch { }
  160. }
  161. }
  162. return record;
  163. }
  164. #endregion
  165. #region IOrm
  166. /// <summary>查询记录。</summary>
  167. /// <param name="database">数据库对象。</param>
  168. /// <param name="model">记录模型。</param>
  169. /// <param name="sql">SQL 语句。</param>
  170. public static Result<object[]> Query(IDbClientAdo database, Type model, string sql)
  171. {
  172. if (database == null) return new Result<object[]>("数据库无效。");
  173. if (model == null) return new Result<object[]>("模型类型无效。");
  174. if (string.IsNullOrEmpty(sql)) return new Result<object[]>("SQL 语句无效。");
  175. using (var query = database.Query(sql))
  176. {
  177. if (query == null) return new Result<object[]>("查询实例无效。");
  178. if (query.Table == null)
  179. {
  180. if (!string.IsNullOrEmpty(query.Message)) return new Result<object[]>(query.Message);
  181. return new Result<object[]>("查询实例不包含数据表。");
  182. }
  183. try
  184. {
  185. var array = Fill(query, model);
  186. return new Result<object[]>(array);
  187. }
  188. catch (Exception ex)
  189. {
  190. return new Result<object[]>(ex);
  191. }
  192. }
  193. }
  194. // /// <summary>查询记录。</summary>
  195. // /// <typeparam name="T">记录模型。</typeparam>
  196. // /// <param name="database">数据库对象。</param>
  197. // /// <param name="sql">SQL 语句。</param>
  198. // public static Result<T[]> Query<T>(IDbClientAdo database, string sql) where T : class, new() => As<object, T>(Query(database, typeof(T), sql));
  199. /// <summary>查询记录。</summary>
  200. /// <param name="database">数据库对象。</param>
  201. /// <param name="model">记录模型。</param>
  202. /// <param name="sqlGetter">生成 SQL 语句的函数,传入参数为表名。</param>
  203. public static Result<object[]> Query(IDbClientAdo database, Type model, Func<string, string> sqlGetter)
  204. {
  205. if (sqlGetter == null) return new Result<object[]>("SQL 语句获取函数无效。");
  206. try
  207. {
  208. var tableName = TableStructure.Parse(model).Name;
  209. if (string.IsNullOrEmpty(tableName)) return new Result<object[]>("表名无效。");
  210. return Query(database, model, sqlGetter(tableName));
  211. }
  212. catch (Exception ex)
  213. {
  214. return new Result<object[]>(ex);
  215. }
  216. }
  217. /// <summary>查询记录。</summary>
  218. /// <typeparam name="T">记录模型。</typeparam>
  219. /// <param name="database">数据库对象。</param>
  220. /// <param name="sqlGetter">生成 SQL 语句的函数,传入参数为表名。</param>
  221. public static Result<T[]> Query<T>(IDbClientAdo database, Func<string, string> sqlGetter) where T : class, new() => As<object, T>(Query(database, typeof(T), sqlGetter));
  222. /// <summary>获取具有指定主键的记录。</summary>
  223. /// <param name="database">数据库对象。</param>
  224. /// <param name="model">记录模型。</param>
  225. /// <param name="key">主键。</param>
  226. /// <param name="sqlGetter">生成 SQL 语句的函数,传入参数为表名和主键值。</param>
  227. public static Result<object> Get(IDbClientAdo database, Type model, string key, Func<string, string, string> sqlGetter)
  228. {
  229. if (sqlGetter == null) return new Result<object>("SQL 语句获取函数无效。");
  230. var safetyKey = TextUtility.SafeKey(key);
  231. if (string.IsNullOrEmpty(safetyKey)) return new Result<object>("主键无效。");
  232. var query = null as IQuery;
  233. var record = null as object;
  234. try
  235. {
  236. record = Activator.CreateInstance(model);
  237. var ts = TableStructure.Parse(model);
  238. var tableName = ts.Name;
  239. if (string.IsNullOrEmpty(tableName)) return new Result<object>("表名无效。");
  240. var sql = sqlGetter(tableName, safetyKey);
  241. query = database.Query(sql);
  242. if (query.Table == null) return new Result<object>("没有获取到记录。");
  243. record = Row(query, 0, model, ts);
  244. }
  245. catch (Exception ex)
  246. {
  247. RuntimeUtility.Dispose(query);
  248. return new Result<object>(ex);
  249. }
  250. RuntimeUtility.Dispose(query);
  251. if (record == null) return new Result<object>("没有获取到记录。");
  252. return new Result<object>(record);
  253. }
  254. /// <summary>获取具有指定主键的记录。</summary>
  255. /// <typeparam name="T">记录模型。</typeparam>
  256. /// <param name="database">数据库对象。</param>
  257. /// <param name="key">主键。</param>
  258. /// <param name="sqlGetter">生成 SQL 语句的函数,传入参数为表名和主键值。</param>
  259. public static Result<T> Get<T>(IDbClientAdo database, string key, Func<string, string, string> sqlGetter) where T : class, IRecord, new() => As<object, T>(Get(database, typeof(T), key, sqlGetter));
  260. /// <summary>获取主键。</summary>
  261. /// <param name="database">数据库对象。</param>
  262. /// <param name="model">记录模型。</param>
  263. /// <param name="sqlGetter">生成 SQL 语句的函数,传入参数为表名。</param>
  264. public static Result<string[]> Keys(IDbClientAdo database, Type model, Func<string, string> sqlGetter)
  265. {
  266. if (database == null) return new Result<string[]>("数据库无效。");
  267. if (model == null) return new Result<string[]>("模型类型无效。");
  268. if (sqlGetter == null) return new Result<string[]>("SQL 语句获取函数无效。");
  269. var tableStructure = null as TableStructure;
  270. try
  271. {
  272. tableStructure = TableStructure.Parse(model);
  273. }
  274. catch (Exception ex)
  275. {
  276. return new Result<string[]>(ex);
  277. }
  278. var tableName = tableStructure.Name;
  279. if (string.IsNullOrEmpty(tableName)) return new Result<string[]>("表名无效。");
  280. // var keyName = null as string;
  281. // foreach (var column in tableStructure.Columns)
  282. // {
  283. // if (column.Key == "Key")
  284. // {
  285. // keyName = column.Value.Field;
  286. // break;
  287. // }
  288. // }
  289. // if (string.IsNullOrEmpty(keyName)) return Result<List<string>>.Error("主键字段无效。");
  290. // var sql = sqlGetter(tableName, keyName);
  291. var sql = sqlGetter(tableName);
  292. var query = null as IQuery;
  293. try
  294. {
  295. query = database.Query(sql);
  296. if (query == null) return new Result<string[]>("查询实例无效。");
  297. var list = new List<string>(query.Rows);
  298. for (var r = 0; r < query.Rows; r++)
  299. {
  300. var key = TextUtility.SafeKey(query.Text(r));
  301. if (string.IsNullOrEmpty(key)) continue;
  302. list.Add(key);
  303. }
  304. query.Dispose();
  305. list.Capacity = list.Count;
  306. var array = list.ToArray();
  307. return new Result<string[]>(array);
  308. }
  309. catch (Exception ex)
  310. {
  311. RuntimeUtility.Dispose(query);
  312. return new Result<string[]>(ex);
  313. }
  314. }
  315. /// <summary>获取主键。</summary>
  316. /// <typeparam name="T">记录模型。</typeparam>
  317. /// <param name="database">数据库对象。</param>
  318. /// <param name="sqlGetter">生成 SQL 语句的函数,传入参数为表名。</param>
  319. public static Result<string[]> Keys<T>(IDbClientAdo database, Func<string, string> sqlGetter) where T : IRecord
  320. {
  321. return Keys(database, typeof(T), sqlGetter);
  322. }
  323. #endregion
  324. #region Record
  325. /// <summary>修复记录属性。</summary>
  326. public static void FixProperties(object record)
  327. {
  328. if (record == null) return;
  329. if (record is IRecord key)
  330. {
  331. if (string.IsNullOrEmpty(key.Key)) key.ResetKey();
  332. }
  333. var now = DateTime.Now;
  334. if (record is IRecordMoment moment)
  335. {
  336. if (string.IsNullOrEmpty(moment.Created)) moment.Created = now.Lucid();
  337. if (string.IsNullOrEmpty(moment.Updated)) moment.Updated = now.Lucid();
  338. }
  339. if (record is IRecordStamp stamp)
  340. {
  341. var utc = ClockUtility.UtcStamp;
  342. if (stamp.Created == 0L) stamp.Created = now.Stamp();
  343. if (stamp.Updated == 0L) stamp.Updated = now.Stamp();
  344. }
  345. }
  346. /// <summary>设置 Updated 属性。</summary>
  347. /// <returns>TRUE:设置成功;FALSE:设置失败。</returns>
  348. public static bool SetUpdated(object record)
  349. {
  350. if (record == null) return false;
  351. var now = DateTime.Now;
  352. var setted = false;
  353. if (record is IRecordMoment moment)
  354. {
  355. moment.Updated = now.Lucid();
  356. setted = true;
  357. }
  358. if (record is IRecordStamp stamp)
  359. {
  360. stamp.Updated = now.Stamp();
  361. setted = true;
  362. }
  363. return setted;
  364. }
  365. #endregion
  366. }
  367. }